diff --git a/examples/rewards/main.go b/examples/rewards/main.go index 350062a1..51bfaafb 100644 --- a/examples/rewards/main.go +++ b/examples/rewards/main.go @@ -2,6 +2,8 @@ package main import ( "context" + "crypto/ed25519" + "crypto/rand" "fmt" "log" "os" @@ -10,6 +12,7 @@ import ( v1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" "github.com/OpenAudio/go-openaudio/pkg/common" "github.com/OpenAudio/go-openaudio/pkg/sdk" + "github.com/mr-tron/base58/base58" ) func main() { @@ -38,15 +41,25 @@ func main() { currentHeight := resp.Msg.ChainInfo.CurrentHeight deadline := currentHeight + 100 + rmPubKey, rmPrivKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + log.Fatalf("Failed to generate RM keypair: %v", err) + } + rewardsManagerPubkey := base58.Encode(rmPubKey) + + if _, err := oap.Rewards.CreateRewardPool(context.Background(), &v1.CreateRewardPool{ + RewardsManagerPubkey: rewardsManagerPubkey, + Authorities: []string{oap.Address()}, + }, rmPrivKey, deadline); err != nil { + log.Fatalf("Failed to create reward pool: %v", err) + } + reward, err := oap.Rewards.CreateReward(context.Background(), &v1.CreateReward{ - RewardId: "reward1", - Name: "Test Reward 1", - Amount: 1000, - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: oap.Address(), Name: "Alec"}, - }, - DeadlineBlockHeight: deadline, - }) + RewardId: "reward1", + Name: "Test Reward 1", + Amount: 1000, + RewardsManagerPubkey: rewardsManagerPubkey, + }, deadline) if err != nil { log.Fatalf("Failed to create reward: %v", err) } diff --git a/pkg/api/core/v1/service.pb.go b/pkg/api/core/v1/service.pb.go index ef0b0cd2..64dac3a6 100644 --- a/pkg/api/core/v1/service.pb.go +++ b/pkg/api/core/v1/service.pb.go @@ -25,7 +25,7 @@ var file_core_v1_service_proto_rawDesc = []byte{ 0x0a, 0x15, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x13, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x84, 0x11, 0x0a, 0x0b, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xd6, 0x11, 0x0a, 0x0b, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, @@ -123,49 +123,54 @@ var file_core_v1_service_proto_rawDesc = []byte{ 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 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, 0x22, 0x00, 0x12, - 0x77, 0x0a, 0x1a, 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, 0x12, 0x2a, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 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, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x53, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 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, 0x1a, - 0x31, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 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, 0x22, + 0x00, 0x12, 0x77, 0x0a, 0x1a, 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, 0x12, + 0x2a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 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, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x20, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x53, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x30, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 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, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 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, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, - 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, - 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x63, 0x6f, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 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, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 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, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, + 0x43, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, + 0x43, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, + 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x1c, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 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, + 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 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 file_core_v1_service_proto_goTypes = []interface{}{ @@ -188,37 +193,39 @@ var file_core_v1_service_proto_goTypes = []interface{}{ (*GetPIERequest)(nil), // 16: core.v1.GetPIERequest (*GetRewardRequest)(nil), // 17: core.v1.GetRewardRequest (*GetRewardsRequest)(nil), // 18: core.v1.GetRewardsRequest - (*GetRewardAttestationRequest)(nil), // 19: core.v1.GetRewardAttestationRequest - (*GetRewardSenderAttestationRequest)(nil), // 20: core.v1.GetRewardSenderAttestationRequest - (*GetDeleteRewardSenderAttestationRequest)(nil), // 21: core.v1.GetDeleteRewardSenderAttestationRequest - (*GetStreamURLsRequest)(nil), // 22: core.v1.GetStreamURLsRequest - (*GetUploadByCIDRequest)(nil), // 23: core.v1.GetUploadByCIDRequest - (*StreamBlocksRequest)(nil), // 24: core.v1.StreamBlocksRequest - (*PingResponse)(nil), // 25: core.v1.PingResponse - (*GetHealthResponse)(nil), // 26: core.v1.GetHealthResponse - (*GetStatusResponse)(nil), // 27: core.v1.GetStatusResponse - (*GetNodeInfoResponse)(nil), // 28: core.v1.GetNodeInfoResponse - (*GetBlockResponse)(nil), // 29: core.v1.GetBlockResponse - (*GetBlocksResponse)(nil), // 30: core.v1.GetBlocksResponse - (*GetTransactionResponse)(nil), // 31: core.v1.GetTransactionResponse - (*SendTransactionResponse)(nil), // 32: core.v1.SendTransactionResponse - (*ForwardTransactionResponse)(nil), // 33: core.v1.ForwardTransactionResponse - (*GetRegistrationAttestationResponse)(nil), // 34: core.v1.GetRegistrationAttestationResponse - (*GetDeregistrationAttestationResponse)(nil), // 35: core.v1.GetDeregistrationAttestationResponse - (*GetStoredSnapshotsResponse)(nil), // 36: core.v1.GetStoredSnapshotsResponse - (*GetSlashAttestationResponse)(nil), // 37: core.v1.GetSlashAttestationResponse - (*GetSlashAttestationsResponse)(nil), // 38: core.v1.GetSlashAttestationsResponse - (*GetERNResponse)(nil), // 39: core.v1.GetERNResponse - (*GetMEADResponse)(nil), // 40: core.v1.GetMEADResponse - (*GetPIEResponse)(nil), // 41: core.v1.GetPIEResponse - (*GetRewardResponse)(nil), // 42: core.v1.GetRewardResponse - (*GetRewardsResponse)(nil), // 43: core.v1.GetRewardsResponse - (*GetRewardAttestationResponse)(nil), // 44: core.v1.GetRewardAttestationResponse - (*GetRewardSenderAttestationResponse)(nil), // 45: core.v1.GetRewardSenderAttestationResponse - (*GetDeleteRewardSenderAttestationResponse)(nil), // 46: core.v1.GetDeleteRewardSenderAttestationResponse - (*GetStreamURLsResponse)(nil), // 47: core.v1.GetStreamURLsResponse - (*GetUploadByCIDResponse)(nil), // 48: core.v1.GetUploadByCIDResponse - (*StreamBlocksResponse)(nil), // 49: core.v1.StreamBlocksResponse + (*GetRewardPoolRequest)(nil), // 19: core.v1.GetRewardPoolRequest + (*GetRewardAttestationRequest)(nil), // 20: core.v1.GetRewardAttestationRequest + (*GetRewardSenderAttestationRequest)(nil), // 21: core.v1.GetRewardSenderAttestationRequest + (*GetDeleteRewardSenderAttestationRequest)(nil), // 22: core.v1.GetDeleteRewardSenderAttestationRequest + (*GetStreamURLsRequest)(nil), // 23: core.v1.GetStreamURLsRequest + (*GetUploadByCIDRequest)(nil), // 24: core.v1.GetUploadByCIDRequest + (*StreamBlocksRequest)(nil), // 25: core.v1.StreamBlocksRequest + (*PingResponse)(nil), // 26: core.v1.PingResponse + (*GetHealthResponse)(nil), // 27: core.v1.GetHealthResponse + (*GetStatusResponse)(nil), // 28: core.v1.GetStatusResponse + (*GetNodeInfoResponse)(nil), // 29: core.v1.GetNodeInfoResponse + (*GetBlockResponse)(nil), // 30: core.v1.GetBlockResponse + (*GetBlocksResponse)(nil), // 31: core.v1.GetBlocksResponse + (*GetTransactionResponse)(nil), // 32: core.v1.GetTransactionResponse + (*SendTransactionResponse)(nil), // 33: core.v1.SendTransactionResponse + (*ForwardTransactionResponse)(nil), // 34: core.v1.ForwardTransactionResponse + (*GetRegistrationAttestationResponse)(nil), // 35: core.v1.GetRegistrationAttestationResponse + (*GetDeregistrationAttestationResponse)(nil), // 36: core.v1.GetDeregistrationAttestationResponse + (*GetStoredSnapshotsResponse)(nil), // 37: core.v1.GetStoredSnapshotsResponse + (*GetSlashAttestationResponse)(nil), // 38: core.v1.GetSlashAttestationResponse + (*GetSlashAttestationsResponse)(nil), // 39: core.v1.GetSlashAttestationsResponse + (*GetERNResponse)(nil), // 40: core.v1.GetERNResponse + (*GetMEADResponse)(nil), // 41: core.v1.GetMEADResponse + (*GetPIEResponse)(nil), // 42: core.v1.GetPIEResponse + (*GetRewardResponse)(nil), // 43: core.v1.GetRewardResponse + (*GetRewardsResponse)(nil), // 44: core.v1.GetRewardsResponse + (*GetRewardPoolResponse)(nil), // 45: core.v1.GetRewardPoolResponse + (*GetRewardAttestationResponse)(nil), // 46: core.v1.GetRewardAttestationResponse + (*GetRewardSenderAttestationResponse)(nil), // 47: core.v1.GetRewardSenderAttestationResponse + (*GetDeleteRewardSenderAttestationResponse)(nil), // 48: core.v1.GetDeleteRewardSenderAttestationResponse + (*GetStreamURLsResponse)(nil), // 49: core.v1.GetStreamURLsResponse + (*GetUploadByCIDResponse)(nil), // 50: core.v1.GetUploadByCIDResponse + (*StreamBlocksResponse)(nil), // 51: core.v1.StreamBlocksResponse } var file_core_v1_service_proto_depIdxs = []int32{ 0, // 0: core.v1.CoreService.Ping:input_type -> core.v1.PingRequest @@ -240,39 +247,41 @@ var file_core_v1_service_proto_depIdxs = []int32{ 16, // 16: core.v1.CoreService.GetPIE:input_type -> core.v1.GetPIERequest 17, // 17: core.v1.CoreService.GetReward:input_type -> core.v1.GetRewardRequest 18, // 18: core.v1.CoreService.GetRewards:input_type -> core.v1.GetRewardsRequest - 19, // 19: core.v1.CoreService.GetRewardAttestation:input_type -> core.v1.GetRewardAttestationRequest - 20, // 20: core.v1.CoreService.GetRewardSenderAttestation:input_type -> core.v1.GetRewardSenderAttestationRequest - 21, // 21: core.v1.CoreService.GetDeleteRewardSenderAttestation:input_type -> core.v1.GetDeleteRewardSenderAttestationRequest - 22, // 22: core.v1.CoreService.GetStreamURLs:input_type -> core.v1.GetStreamURLsRequest - 23, // 23: core.v1.CoreService.GetUploadByCID:input_type -> core.v1.GetUploadByCIDRequest - 24, // 24: core.v1.CoreService.StreamBlocks:input_type -> core.v1.StreamBlocksRequest - 25, // 25: core.v1.CoreService.Ping:output_type -> core.v1.PingResponse - 26, // 26: core.v1.CoreService.GetHealth:output_type -> core.v1.GetHealthResponse - 27, // 27: core.v1.CoreService.GetStatus:output_type -> core.v1.GetStatusResponse - 28, // 28: core.v1.CoreService.GetNodeInfo:output_type -> core.v1.GetNodeInfoResponse - 29, // 29: core.v1.CoreService.GetBlock:output_type -> core.v1.GetBlockResponse - 30, // 30: core.v1.CoreService.GetBlocks:output_type -> core.v1.GetBlocksResponse - 31, // 31: core.v1.CoreService.GetTransaction:output_type -> core.v1.GetTransactionResponse - 32, // 32: core.v1.CoreService.SendTransaction:output_type -> core.v1.SendTransactionResponse - 33, // 33: core.v1.CoreService.ForwardTransaction:output_type -> core.v1.ForwardTransactionResponse - 34, // 34: core.v1.CoreService.GetRegistrationAttestation:output_type -> core.v1.GetRegistrationAttestationResponse - 35, // 35: core.v1.CoreService.GetDeregistrationAttestation:output_type -> core.v1.GetDeregistrationAttestationResponse - 36, // 36: core.v1.CoreService.GetStoredSnapshots:output_type -> core.v1.GetStoredSnapshotsResponse - 37, // 37: core.v1.CoreService.GetSlashAttestation:output_type -> core.v1.GetSlashAttestationResponse - 38, // 38: core.v1.CoreService.GetSlashAttestations:output_type -> core.v1.GetSlashAttestationsResponse - 39, // 39: core.v1.CoreService.GetERN:output_type -> core.v1.GetERNResponse - 40, // 40: core.v1.CoreService.GetMEAD:output_type -> core.v1.GetMEADResponse - 41, // 41: core.v1.CoreService.GetPIE:output_type -> core.v1.GetPIEResponse - 42, // 42: core.v1.CoreService.GetReward:output_type -> core.v1.GetRewardResponse - 43, // 43: core.v1.CoreService.GetRewards:output_type -> core.v1.GetRewardsResponse - 44, // 44: core.v1.CoreService.GetRewardAttestation:output_type -> core.v1.GetRewardAttestationResponse - 45, // 45: core.v1.CoreService.GetRewardSenderAttestation:output_type -> core.v1.GetRewardSenderAttestationResponse - 46, // 46: core.v1.CoreService.GetDeleteRewardSenderAttestation:output_type -> core.v1.GetDeleteRewardSenderAttestationResponse - 47, // 47: core.v1.CoreService.GetStreamURLs:output_type -> core.v1.GetStreamURLsResponse - 48, // 48: core.v1.CoreService.GetUploadByCID:output_type -> core.v1.GetUploadByCIDResponse - 49, // 49: core.v1.CoreService.StreamBlocks:output_type -> core.v1.StreamBlocksResponse - 25, // [25:50] is the sub-list for method output_type - 0, // [0:25] is the sub-list for method input_type + 19, // 19: core.v1.CoreService.GetRewardPool:input_type -> core.v1.GetRewardPoolRequest + 20, // 20: core.v1.CoreService.GetRewardAttestation:input_type -> core.v1.GetRewardAttestationRequest + 21, // 21: core.v1.CoreService.GetRewardSenderAttestation:input_type -> core.v1.GetRewardSenderAttestationRequest + 22, // 22: core.v1.CoreService.GetDeleteRewardSenderAttestation:input_type -> core.v1.GetDeleteRewardSenderAttestationRequest + 23, // 23: core.v1.CoreService.GetStreamURLs:input_type -> core.v1.GetStreamURLsRequest + 24, // 24: core.v1.CoreService.GetUploadByCID:input_type -> core.v1.GetUploadByCIDRequest + 25, // 25: core.v1.CoreService.StreamBlocks:input_type -> core.v1.StreamBlocksRequest + 26, // 26: core.v1.CoreService.Ping:output_type -> core.v1.PingResponse + 27, // 27: core.v1.CoreService.GetHealth:output_type -> core.v1.GetHealthResponse + 28, // 28: core.v1.CoreService.GetStatus:output_type -> core.v1.GetStatusResponse + 29, // 29: core.v1.CoreService.GetNodeInfo:output_type -> core.v1.GetNodeInfoResponse + 30, // 30: core.v1.CoreService.GetBlock:output_type -> core.v1.GetBlockResponse + 31, // 31: core.v1.CoreService.GetBlocks:output_type -> core.v1.GetBlocksResponse + 32, // 32: core.v1.CoreService.GetTransaction:output_type -> core.v1.GetTransactionResponse + 33, // 33: core.v1.CoreService.SendTransaction:output_type -> core.v1.SendTransactionResponse + 34, // 34: core.v1.CoreService.ForwardTransaction:output_type -> core.v1.ForwardTransactionResponse + 35, // 35: core.v1.CoreService.GetRegistrationAttestation:output_type -> core.v1.GetRegistrationAttestationResponse + 36, // 36: core.v1.CoreService.GetDeregistrationAttestation:output_type -> core.v1.GetDeregistrationAttestationResponse + 37, // 37: core.v1.CoreService.GetStoredSnapshots:output_type -> core.v1.GetStoredSnapshotsResponse + 38, // 38: core.v1.CoreService.GetSlashAttestation:output_type -> core.v1.GetSlashAttestationResponse + 39, // 39: core.v1.CoreService.GetSlashAttestations:output_type -> core.v1.GetSlashAttestationsResponse + 40, // 40: core.v1.CoreService.GetERN:output_type -> core.v1.GetERNResponse + 41, // 41: core.v1.CoreService.GetMEAD:output_type -> core.v1.GetMEADResponse + 42, // 42: core.v1.CoreService.GetPIE:output_type -> core.v1.GetPIEResponse + 43, // 43: core.v1.CoreService.GetReward:output_type -> core.v1.GetRewardResponse + 44, // 44: core.v1.CoreService.GetRewards:output_type -> core.v1.GetRewardsResponse + 45, // 45: core.v1.CoreService.GetRewardPool:output_type -> core.v1.GetRewardPoolResponse + 46, // 46: core.v1.CoreService.GetRewardAttestation:output_type -> core.v1.GetRewardAttestationResponse + 47, // 47: core.v1.CoreService.GetRewardSenderAttestation:output_type -> core.v1.GetRewardSenderAttestationResponse + 48, // 48: core.v1.CoreService.GetDeleteRewardSenderAttestation:output_type -> core.v1.GetDeleteRewardSenderAttestationResponse + 49, // 49: core.v1.CoreService.GetStreamURLs:output_type -> core.v1.GetStreamURLsResponse + 50, // 50: core.v1.CoreService.GetUploadByCID:output_type -> core.v1.GetUploadByCIDResponse + 51, // 51: core.v1.CoreService.StreamBlocks:output_type -> core.v1.StreamBlocksResponse + 26, // [26:52] is the sub-list for method output_type + 0, // [0:26] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/pkg/api/core/v1/types.pb.go b/pkg/api/core/v1/types.pb.go index a44571f9..09034063 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_RewardPool Transaction isSignedTransaction_Transaction `protobuf_oneof:"transaction"` } @@ -1633,6 +1634,13 @@ func (x *SignedTransaction) GetFileUpload() *FileUpload { return nil } +func (x *SignedTransaction) GetRewardPool() *RewardPoolMessage { + if x, ok := x.GetTransaction().(*SignedTransaction_RewardPool); ok { + return x.RewardPool + } + 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_RewardPool struct { + RewardPool *RewardPoolMessage `protobuf:"bytes,1011,opt,name=reward_pool,json=rewardPool,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_RewardPool) isSignedTransaction_Transaction() {} + type TrackPlays struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4096,16 +4110,24 @@ func (x *GetPIEResponse) GetPie() *v1beta11.PieMessage { return nil } +// RewardMessage is the wire envelope: it bundles a signed body with its +// signature. The body is signed by first taking the deterministic protobuf +// marshaling of body, then computing sha256 over those bytes, and signing +// that digest; the signature lives alongside the body, not inside it, so +// signing has no chicken-and-egg. type RewardMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Types that are assignable to Action: - // - // *RewardMessage_Create - // *RewardMessage_Delete - Action isRewardMessage_Action `protobuf_oneof:"action"` + Body *RewardBody `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + // secp256k1 (Ethereum-style) signature, hex-encoded. The signed + // digest is sha256(deterministic-protobuf-marshaling of body) — i.e., + // marshal body with proto.MarshalOptions{Deterministic: true}, then + // sha256-hash, then crypto.Sign as eth-recoverable. Non-Go clients + // must reproduce this pre-hash (sha256, NOT keccak256) to produce a + // signature the validator will accept. + Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` } func (x *RewardMessage) Reset() { @@ -4140,58 +4162,40 @@ func (*RewardMessage) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{65} } -func (m *RewardMessage) GetAction() isRewardMessage_Action { - if m != nil { - return m.Action - } - return nil -} - -func (x *RewardMessage) GetCreate() *CreateReward { - if x, ok := x.GetAction().(*RewardMessage_Create); ok { - return x.Create +func (x *RewardMessage) GetBody() *RewardBody { + if x != nil { + return x.Body } return nil } -func (x *RewardMessage) GetDelete() *DeleteReward { - if x, ok := x.GetAction().(*RewardMessage_Delete); ok { - return x.Delete +func (x *RewardMessage) GetSignature() string { + if x != nil { + return x.Signature } - return nil -} - -type isRewardMessage_Action interface { - isRewardMessage_Action() -} - -type RewardMessage_Create struct { - Create *CreateReward `protobuf:"bytes,1000,opt,name=create,proto3,oneof"` -} - -type RewardMessage_Delete struct { - Delete *DeleteReward `protobuf:"bytes,1001,opt,name=delete,proto3,oneof"` + return "" } -func (*RewardMessage_Create) isRewardMessage_Action() {} - -func (*RewardMessage_Delete) isRewardMessage_Action() {} - -type CreateReward struct { +// RewardBody is the signed payload. deadline_block_height bounds the +// signature's validity (the validator rejects after expiry); the action +// is the actual operation. The oneof tag discriminates Create from +// Delete so two actions with otherwise-identical inner shapes produce +// different bytes. +type RewardBody struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RewardId string `protobuf:"bytes,1,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` - ClaimAuthorities []*ClaimAuthority `protobuf:"bytes,4,rep,name=claim_authorities,json=claimAuthorities,proto3" json:"claim_authorities,omitempty"` - DeadlineBlockHeight int64 `protobuf:"varint,5,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` - Signature string `protobuf:"bytes,6,opt,name=signature,proto3" json:"signature,omitempty"` // Signature over deterministic reward data + DeadlineBlockHeight int64 `protobuf:"varint,1,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` + // Types that are assignable to Action: + // + // *RewardBody_Create + // *RewardBody_Delete + Action isRewardBody_Action `protobuf_oneof:"action"` } -func (x *CreateReward) Reset() { - *x = CreateReward{} +func (x *RewardBody) Reset() { + *x = RewardBody{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4199,13 +4203,13 @@ func (x *CreateReward) Reset() { } } -func (x *CreateReward) String() string { +func (x *RewardBody) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateReward) ProtoMessage() {} +func (*RewardBody) ProtoMessage() {} -func (x *CreateReward) ProtoReflect() protoreflect.Message { +func (x *RewardBody) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4217,65 +4221,74 @@ func (x *CreateReward) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateReward.ProtoReflect.Descriptor instead. -func (*CreateReward) Descriptor() ([]byte, []int) { +// Deprecated: Use RewardBody.ProtoReflect.Descriptor instead. +func (*RewardBody) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{66} } -func (x *CreateReward) GetRewardId() string { +func (x *RewardBody) GetDeadlineBlockHeight() int64 { if x != nil { - return x.RewardId + return x.DeadlineBlockHeight } - return "" + return 0 } -func (x *CreateReward) GetName() string { - if x != nil { - return x.Name +func (m *RewardBody) GetAction() isRewardBody_Action { + if m != nil { + return m.Action } - return "" + return nil } -func (x *CreateReward) GetAmount() uint64 { - if x != nil { - return x.Amount +func (x *RewardBody) GetCreate() *CreateReward { + if x, ok := x.GetAction().(*RewardBody_Create); ok { + return x.Create } - return 0 + return nil } -func (x *CreateReward) GetClaimAuthorities() []*ClaimAuthority { - if x != nil { - return x.ClaimAuthorities +func (x *RewardBody) GetDelete() *DeleteReward { + if x, ok := x.GetAction().(*RewardBody_Delete); ok { + return x.Delete } return nil } -func (x *CreateReward) GetDeadlineBlockHeight() int64 { - if x != nil { - return x.DeadlineBlockHeight - } - return 0 +type isRewardBody_Action interface { + isRewardBody_Action() } -func (x *CreateReward) GetSignature() string { - if x != nil { - return x.Signature - } - return "" +type RewardBody_Create struct { + Create *CreateReward `protobuf:"bytes,1000,opt,name=create,proto3,oneof"` } -type DeleteReward struct { +type RewardBody_Delete struct { + Delete *DeleteReward `protobuf:"bytes,1001,opt,name=delete,proto3,oneof"` +} + +func (*RewardBody_Create) isRewardBody_Action() {} + +func (*RewardBody_Delete) isRewardBody_Action() {} + +type CreateReward struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // The deployed reward address - DeadlineBlockHeight int64 `protobuf:"varint,2,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` - Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` // Signature over deterministic delete data + RewardId string `protobuf:"bytes,1,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + // Solana reward manager pubkey (base58, 32 bytes). Identifies the pool + // whose authorities are allowed to issue rewards in this RM. The + // recovered signer must be a current member of pool.authorities; the + // reward inherits attestation rights from the pool going forward. + // The pool itself is identified by this same pubkey: pool address == + // rewards_manager_pubkey for first-class pools. + RewardsManagerPubkey string `protobuf:"bytes,7,opt,name=rewards_manager_pubkey,json=rewardsManagerPubkey,proto3" json:"rewards_manager_pubkey,omitempty"` } -func (x *DeleteReward) Reset() { - *x = DeleteReward{} +func (x *CreateReward) Reset() { + *x = CreateReward{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4283,13 +4296,13 @@ func (x *DeleteReward) Reset() { } } -func (x *DeleteReward) String() string { +func (x *CreateReward) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteReward) ProtoMessage() {} +func (*CreateReward) ProtoMessage() {} -func (x *DeleteReward) ProtoReflect() protoreflect.Message { +func (x *CreateReward) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4301,43 +4314,49 @@ func (x *DeleteReward) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteReward.ProtoReflect.Descriptor instead. -func (*DeleteReward) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateReward.ProtoReflect.Descriptor instead. +func (*CreateReward) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{67} } -func (x *DeleteReward) GetAddress() string { +func (x *CreateReward) GetRewardId() string { if x != nil { - return x.Address + return x.RewardId + } + return "" +} + +func (x *CreateReward) GetName() string { + if x != nil { + return x.Name } return "" } -func (x *DeleteReward) GetDeadlineBlockHeight() int64 { +func (x *CreateReward) GetAmount() uint64 { if x != nil { - return x.DeadlineBlockHeight + return x.Amount } return 0 } -func (x *DeleteReward) GetSignature() string { +func (x *CreateReward) GetRewardsManagerPubkey() string { if x != nil { - return x.Signature + return x.RewardsManagerPubkey } return "" } -type GetRewardRequest struct { +type DeleteReward struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // The deployed reward address - Txhash string `protobuf:"bytes,2,opt,name=txhash,proto3" json:"txhash,omitempty"` } -func (x *GetRewardRequest) Reset() { - *x = GetRewardRequest{} +func (x *DeleteReward) Reset() { + *x = DeleteReward{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4345,13 +4364,13 @@ func (x *GetRewardRequest) Reset() { } } -func (x *GetRewardRequest) String() string { +func (x *DeleteReward) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRewardRequest) ProtoMessage() {} +func (*DeleteReward) ProtoMessage() {} -func (x *GetRewardRequest) ProtoReflect() protoreflect.Message { +func (x *DeleteReward) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4363,41 +4382,50 @@ func (x *GetRewardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRewardRequest.ProtoReflect.Descriptor instead. -func (*GetRewardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteReward.ProtoReflect.Descriptor instead. +func (*DeleteReward) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{68} } -func (x *GetRewardRequest) GetAddress() string { +func (x *DeleteReward) GetAddress() string { if x != nil { return x.Address } return "" } -func (x *GetRewardRequest) GetTxhash() string { - if x != nil { - return x.Txhash - } - return "" -} - -type GetRewardResponse struct { +// RewardPoolMessage is the wire envelope for pool-management transactions. +// Two signatures cover the same body bytes (`deterministic-protobuf- +// marshaling of body`); the schemes differ in pre-hashing: +// +// - signature: secp256k1 (eth) recoverable signature, hex-encoded. +// Pre-hash is sha256(body_bytes) — see RewardMessage.signature for +// the cross-language reproduction recipe. Identifies the fee-paying +// tx submitter who must be in the pool's current authority set +// (Create: initial authorities; SetAuthorities: existing pool +// authorities). +// - rm_owner_signature: ed25519 signature over body_bytes directly +// (ed25519 handles its own internal hashing — sign body_bytes raw, +// do NOT pre-hash). REQUIRED when body.action is Create. +// Verification key IS body.create.rewards_manager_pubkey (the +// Solana RM state account is itself an ed25519 keypair). Proves +// possession of the RM keypair, defeating pool-creation +// frontrunning by an observer who watches Solana RM init events +// but lacks the launchpad's deterministic-secret. Ignored for +// SetAuthorities because rotation is meant to work without the +// launchpad. +type RewardPoolMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - RewardId string `protobuf:"bytes,2,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - Amount uint64 `protobuf:"varint,4,opt,name=amount,proto3" json:"amount,omitempty"` - ClaimAuthorities []string `protobuf:"bytes,5,rep,name=claim_authorities,json=claimAuthorities,proto3" json:"claim_authorities,omitempty"` - Sender string `protobuf:"bytes,6,opt,name=sender,proto3" json:"sender,omitempty"` - BlockHeight int64 `protobuf:"varint,7,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Body *RewardPoolBody `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + RmOwnerSignature []byte `protobuf:"bytes,3,opt,name=rm_owner_signature,json=rmOwnerSignature,proto3" json:"rm_owner_signature,omitempty"` } -func (x *GetRewardResponse) Reset() { - *x = GetRewardResponse{} +func (x *RewardPoolMessage) Reset() { + *x = RewardPoolMessage{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4405,13 +4433,13 @@ func (x *GetRewardResponse) Reset() { } } -func (x *GetRewardResponse) String() string { +func (x *RewardPoolMessage) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetRewardResponse) ProtoMessage() {} +func (*RewardPoolMessage) ProtoMessage() {} -func (x *GetRewardResponse) ProtoReflect() protoreflect.Message { +func (x *RewardPoolMessage) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4423,75 +4451,56 @@ func (x *GetRewardResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetRewardResponse.ProtoReflect.Descriptor instead. -func (*GetRewardResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use RewardPoolMessage.ProtoReflect.Descriptor instead. +func (*RewardPoolMessage) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{69} } -func (x *GetRewardResponse) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *GetRewardResponse) GetRewardId() string { - if x != nil { - return x.RewardId - } - return "" -} - -func (x *GetRewardResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetRewardResponse) GetAmount() uint64 { - if x != nil { - return x.Amount - } - return 0 -} - -func (x *GetRewardResponse) GetClaimAuthorities() []string { +func (x *RewardPoolMessage) GetBody() *RewardPoolBody { if x != nil { - return x.ClaimAuthorities + return x.Body } return nil } -func (x *GetRewardResponse) GetSender() string { +func (x *RewardPoolMessage) GetSignature() string { if x != nil { - return x.Sender + return x.Signature } return "" } -func (x *GetRewardResponse) GetBlockHeight() int64 { +func (x *RewardPoolMessage) GetRmOwnerSignature() []byte { if x != nil { - return x.BlockHeight + return x.RmOwnerSignature } - return 0 + return nil } -type RewardAttestationSignature struct { +// RewardPoolBody is the signed payload. deadline_block_height bounds the +// signature's validity; action discriminates Create from SetAuthorities +// so two otherwise-identical messages produce different bytes. +// +// Cross-chain replay isn't a threat surface here: each environment's +// launchpad uses a different deterministic secret, so the same +// rewards_manager_pubkey cannot be derived on more than one chain. A +// captured CreateRewardPool replayed on a different chain refers to an +// RM that doesn't exist there. +type RewardPoolBody struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - EthRecipientAddress string `protobuf:"bytes,1,opt,name=eth_recipient_address,json=ethRecipientAddress,proto3" json:"eth_recipient_address,omitempty"` - Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` - RewardAddress string `protobuf:"bytes,3,opt,name=reward_address,json=rewardAddress,proto3" json:"reward_address,omitempty"` // Binds signature to specific deployed reward - RewardId string `protobuf:"bytes,4,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` - Specifier string `protobuf:"bytes,5,opt,name=specifier,proto3" json:"specifier,omitempty"` - ClaimAuthority string `protobuf:"bytes,6,opt,name=claim_authority,json=claimAuthority,proto3" json:"claim_authority,omitempty"` + DeadlineBlockHeight int64 `protobuf:"varint,1,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` + // Types that are assignable to Action: + // + // *RewardPoolBody_Create + // *RewardPoolBody_SetAuthorities + Action isRewardPoolBody_Action `protobuf_oneof:"action"` } -func (x *RewardAttestationSignature) Reset() { - *x = RewardAttestationSignature{} +func (x *RewardPoolBody) Reset() { + *x = RewardPoolBody{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4499,13 +4508,13 @@ func (x *RewardAttestationSignature) Reset() { } } -func (x *RewardAttestationSignature) String() string { +func (x *RewardPoolBody) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RewardAttestationSignature) ProtoMessage() {} +func (*RewardPoolBody) ProtoMessage() {} -func (x *RewardAttestationSignature) ProtoReflect() protoreflect.Message { +func (x *RewardPoolBody) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4517,63 +4526,76 @@ func (x *RewardAttestationSignature) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RewardAttestationSignature.ProtoReflect.Descriptor instead. -func (*RewardAttestationSignature) Descriptor() ([]byte, []int) { +// Deprecated: Use RewardPoolBody.ProtoReflect.Descriptor instead. +func (*RewardPoolBody) Descriptor() ([]byte, []int) { return file_core_v1_types_proto_rawDescGZIP(), []int{70} } -func (x *RewardAttestationSignature) GetEthRecipientAddress() string { +func (x *RewardPoolBody) GetDeadlineBlockHeight() int64 { if x != nil { - return x.EthRecipientAddress + return x.DeadlineBlockHeight } - return "" + return 0 } -func (x *RewardAttestationSignature) GetAmount() uint64 { - if x != nil { - return x.Amount +func (m *RewardPoolBody) GetAction() isRewardPoolBody_Action { + if m != nil { + return m.Action } - return 0 + return nil } -func (x *RewardAttestationSignature) GetRewardAddress() string { - if x != nil { - return x.RewardAddress +func (x *RewardPoolBody) GetCreate() *CreateRewardPool { + if x, ok := x.GetAction().(*RewardPoolBody_Create); ok { + return x.Create } - return "" + return nil } -func (x *RewardAttestationSignature) GetRewardId() string { - if x != nil { - return x.RewardId +func (x *RewardPoolBody) GetSetAuthorities() *SetRewardPoolAuthorities { + if x, ok := x.GetAction().(*RewardPoolBody_SetAuthorities); ok { + return x.SetAuthorities } - return "" + return nil } -func (x *RewardAttestationSignature) GetSpecifier() string { - if x != nil { - return x.Specifier - } - return "" +type isRewardPoolBody_Action interface { + isRewardPoolBody_Action() } -func (x *RewardAttestationSignature) GetClaimAuthority() string { - if x != nil { - return x.ClaimAuthority - } - return "" +type RewardPoolBody_Create struct { + Create *CreateRewardPool `protobuf:"bytes,2000,opt,name=create,proto3,oneof"` } -type UploadSignature struct { +type RewardPoolBody_SetAuthorities struct { + SetAuthorities *SetRewardPoolAuthorities `protobuf:"bytes,2001,opt,name=set_authorities,json=setAuthorities,proto3,oneof"` +} + +func (*RewardPoolBody_Create) isRewardPoolBody_Action() {} + +func (*RewardPoolBody_SetAuthorities) isRewardPoolBody_Action() {} + +type CreateRewardPool struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cid string `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` -} - -func (x *UploadSignature) Reset() { - *x = UploadSignature{} + // The pool is identified by its Solana reward manager pubkey (base58, + // 32 bytes). Subsequent CreateReward / SetRewardPoolAuthorities messages + // and the validator's sender-attestation gate use this same value to + // resolve the pool. There is no separate "pool address" — the pool's + // identity IS the RM pubkey, so pool↔RM binding cannot be set wrong + // by construction. + RewardsManagerPubkey string `protobuf:"bytes,1,opt,name=rewards_manager_pubkey,json=rewardsManagerPubkey,proto3" json:"rewards_manager_pubkey,omitempty"` + // Initial set of eth addresses authorized to attest for rewards in this + // pool. The recovered signer must be a member of this list. Each entry + // must be a valid eth hex address. Frontrunning defense lives at the + // envelope level (RewardPoolMessage.rm_owner_signature). + Authorities []string `protobuf:"bytes,2,rep,name=authorities,proto3" json:"authorities,omitempty"` +} + +func (x *CreateRewardPool) Reset() { + *x = CreateRewardPool{} if protoimpl.UnsafeEnabled { mi := &file_core_v1_types_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4581,13 +4603,13 @@ func (x *UploadSignature) Reset() { } } -func (x *UploadSignature) String() string { +func (x *CreateRewardPool) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UploadSignature) ProtoMessage() {} +func (*CreateRewardPool) ProtoMessage() {} -func (x *UploadSignature) ProtoReflect() protoreflect.Message { +func (x *CreateRewardPool) ProtoReflect() protoreflect.Message { mi := &file_core_v1_types_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4599,9 +4621,704 @@ func (x *UploadSignature) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UploadSignature.ProtoReflect.Descriptor instead. +// Deprecated: Use CreateRewardPool.ProtoReflect.Descriptor instead. +func (*CreateRewardPool) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{71} +} + +func (x *CreateRewardPool) GetRewardsManagerPubkey() string { + if x != nil { + return x.RewardsManagerPubkey + } + return "" +} + +func (x *CreateRewardPool) GetAuthorities() []string { + if x != nil { + return x.Authorities + } + return nil +} + +// SetRewardPoolAuthorities replaces the pool's authority set wholesale. The +// signer must be in the *current* pool.authorities; the new list must be +// non-empty and contain only valid eth addresses (otherwise the pool is +// permanently orphaned). Add and remove are derived views: callers compose +// the new list themselves. +type SetRewardPoolAuthorities struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RewardsManagerPubkey string `protobuf:"bytes,1,opt,name=rewards_manager_pubkey,json=rewardsManagerPubkey,proto3" json:"rewards_manager_pubkey,omitempty"` + Authorities []string `protobuf:"bytes,2,rep,name=authorities,proto3" json:"authorities,omitempty"` +} + +func (x *SetRewardPoolAuthorities) Reset() { + *x = SetRewardPoolAuthorities{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[72] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetRewardPoolAuthorities) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRewardPoolAuthorities) ProtoMessage() {} + +func (x *SetRewardPoolAuthorities) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[72] + 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 SetRewardPoolAuthorities.ProtoReflect.Descriptor instead. +func (*SetRewardPoolAuthorities) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{72} +} + +func (x *SetRewardPoolAuthorities) GetRewardsManagerPubkey() string { + if x != nil { + return x.RewardsManagerPubkey + } + return "" +} + +func (x *SetRewardPoolAuthorities) GetAuthorities() []string { + if x != nil { + return x.Authorities + } + return nil +} + +// === Legacy reward wire format (pre-pool-rollout) === +// +// These messages are the on-the-wire shape used before the body+signature +// envelope was introduced. They are NOT used by new clients or new code paths +// — they exist solely so that the new binary can decode and apply historical +// reward transactions encountered during block-sync-from-genesis. +// +// DO NOT REMOVE these types or their field tags: removing them would break +// replay of any chain history that contains rewards committed before the +// upgrade. Adding new fields here is also forbidden — the wire shape must +// remain pinned to what the old network produced. +type LegacyRewardMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Action: + // + // *LegacyRewardMessage_Create + // *LegacyRewardMessage_Delete + Action isLegacyRewardMessage_Action `protobuf_oneof:"action"` +} + +func (x *LegacyRewardMessage) Reset() { + *x = LegacyRewardMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[73] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LegacyRewardMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LegacyRewardMessage) ProtoMessage() {} + +func (x *LegacyRewardMessage) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[73] + 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 LegacyRewardMessage.ProtoReflect.Descriptor instead. +func (*LegacyRewardMessage) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{73} +} + +func (m *LegacyRewardMessage) GetAction() isLegacyRewardMessage_Action { + if m != nil { + return m.Action + } + return nil +} + +func (x *LegacyRewardMessage) GetCreate() *LegacyCreateReward { + if x, ok := x.GetAction().(*LegacyRewardMessage_Create); ok { + return x.Create + } + return nil +} + +func (x *LegacyRewardMessage) GetDelete() *LegacyDeleteReward { + if x, ok := x.GetAction().(*LegacyRewardMessage_Delete); ok { + return x.Delete + } + return nil +} + +type isLegacyRewardMessage_Action interface { + isLegacyRewardMessage_Action() +} + +type LegacyRewardMessage_Create struct { + Create *LegacyCreateReward `protobuf:"bytes,1000,opt,name=create,proto3,oneof"` +} + +type LegacyRewardMessage_Delete struct { + Delete *LegacyDeleteReward `protobuf:"bytes,1001,opt,name=delete,proto3,oneof"` +} + +func (*LegacyRewardMessage_Create) isLegacyRewardMessage_Action() {} + +func (*LegacyRewardMessage_Delete) isLegacyRewardMessage_Action() {} + +type LegacyCreateReward struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RewardId string `protobuf:"bytes,1,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + ClaimAuthorities []*ClaimAuthority `protobuf:"bytes,4,rep,name=claim_authorities,json=claimAuthorities,proto3" json:"claim_authorities,omitempty"` + DeadlineBlockHeight int64 `protobuf:"varint,5,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` + Signature string `protobuf:"bytes,6,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *LegacyCreateReward) Reset() { + *x = LegacyCreateReward{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[74] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LegacyCreateReward) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LegacyCreateReward) ProtoMessage() {} + +func (x *LegacyCreateReward) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[74] + 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 LegacyCreateReward.ProtoReflect.Descriptor instead. +func (*LegacyCreateReward) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{74} +} + +func (x *LegacyCreateReward) GetRewardId() string { + if x != nil { + return x.RewardId + } + return "" +} + +func (x *LegacyCreateReward) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LegacyCreateReward) GetAmount() uint64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *LegacyCreateReward) GetClaimAuthorities() []*ClaimAuthority { + if x != nil { + return x.ClaimAuthorities + } + return nil +} + +func (x *LegacyCreateReward) GetDeadlineBlockHeight() int64 { + if x != nil { + return x.DeadlineBlockHeight + } + return 0 +} + +func (x *LegacyCreateReward) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +type LegacyDeleteReward struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + DeadlineBlockHeight int64 `protobuf:"varint,2,opt,name=deadline_block_height,json=deadlineBlockHeight,proto3" json:"deadline_block_height,omitempty"` + Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *LegacyDeleteReward) Reset() { + *x = LegacyDeleteReward{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[75] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LegacyDeleteReward) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LegacyDeleteReward) ProtoMessage() {} + +func (x *LegacyDeleteReward) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[75] + 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 LegacyDeleteReward.ProtoReflect.Descriptor instead. +func (*LegacyDeleteReward) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{75} +} + +func (x *LegacyDeleteReward) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *LegacyDeleteReward) GetDeadlineBlockHeight() int64 { + if x != nil { + return x.DeadlineBlockHeight + } + return 0 +} + +func (x *LegacyDeleteReward) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +type GetRewardPoolRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RewardsManagerPubkey string `protobuf:"bytes,1,opt,name=rewards_manager_pubkey,json=rewardsManagerPubkey,proto3" json:"rewards_manager_pubkey,omitempty"` +} + +func (x *GetRewardPoolRequest) Reset() { + *x = GetRewardPoolRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[76] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRewardPoolRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRewardPoolRequest) ProtoMessage() {} + +func (x *GetRewardPoolRequest) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[76] + 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 GetRewardPoolRequest.ProtoReflect.Descriptor instead. +func (*GetRewardPoolRequest) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{76} +} + +func (x *GetRewardPoolRequest) GetRewardsManagerPubkey() string { + if x != nil { + return x.RewardsManagerPubkey + } + return "" +} + +type GetRewardPoolResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RewardsManagerPubkey string `protobuf:"bytes,1,opt,name=rewards_manager_pubkey,json=rewardsManagerPubkey,proto3" json:"rewards_manager_pubkey,omitempty"` + Authorities []string `protobuf:"bytes,2,rep,name=authorities,proto3" json:"authorities,omitempty"` +} + +func (x *GetRewardPoolResponse) Reset() { + *x = GetRewardPoolResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[77] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRewardPoolResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRewardPoolResponse) ProtoMessage() {} + +func (x *GetRewardPoolResponse) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[77] + 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 GetRewardPoolResponse.ProtoReflect.Descriptor instead. +func (*GetRewardPoolResponse) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{77} +} + +func (x *GetRewardPoolResponse) GetRewardsManagerPubkey() string { + if x != nil { + return x.RewardsManagerPubkey + } + return "" +} + +func (x *GetRewardPoolResponse) GetAuthorities() []string { + if x != nil { + return x.Authorities + } + return nil +} + +type GetRewardRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // The deployed reward address + Txhash string `protobuf:"bytes,2,opt,name=txhash,proto3" json:"txhash,omitempty"` +} + +func (x *GetRewardRequest) Reset() { + *x = GetRewardRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[78] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRewardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRewardRequest) ProtoMessage() {} + +func (x *GetRewardRequest) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[78] + 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 GetRewardRequest.ProtoReflect.Descriptor instead. +func (*GetRewardRequest) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{78} +} + +func (x *GetRewardRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *GetRewardRequest) GetTxhash() string { + if x != nil { + return x.Txhash + } + return "" +} + +type GetRewardResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + RewardId string `protobuf:"bytes,2,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Amount uint64 `protobuf:"varint,4,opt,name=amount,proto3" json:"amount,omitempty"` + ClaimAuthorities []string `protobuf:"bytes,5,rep,name=claim_authorities,json=claimAuthorities,proto3" json:"claim_authorities,omitempty"` + Sender string `protobuf:"bytes,6,opt,name=sender,proto3" json:"sender,omitempty"` + BlockHeight int64 `protobuf:"varint,7,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (x *GetRewardResponse) Reset() { + *x = GetRewardResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[79] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRewardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRewardResponse) ProtoMessage() {} + +func (x *GetRewardResponse) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[79] + 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 GetRewardResponse.ProtoReflect.Descriptor instead. +func (*GetRewardResponse) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{79} +} + +func (x *GetRewardResponse) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *GetRewardResponse) GetRewardId() string { + if x != nil { + return x.RewardId + } + return "" +} + +func (x *GetRewardResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetRewardResponse) GetAmount() uint64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *GetRewardResponse) GetClaimAuthorities() []string { + if x != nil { + return x.ClaimAuthorities + } + return nil +} + +func (x *GetRewardResponse) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + +func (x *GetRewardResponse) GetBlockHeight() int64 { + if x != nil { + return x.BlockHeight + } + return 0 +} + +type RewardAttestationSignature struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EthRecipientAddress string `protobuf:"bytes,1,opt,name=eth_recipient_address,json=ethRecipientAddress,proto3" json:"eth_recipient_address,omitempty"` + Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + RewardAddress string `protobuf:"bytes,3,opt,name=reward_address,json=rewardAddress,proto3" json:"reward_address,omitempty"` // Binds signature to specific deployed reward + RewardId string `protobuf:"bytes,4,opt,name=reward_id,json=rewardId,proto3" json:"reward_id,omitempty"` + Specifier string `protobuf:"bytes,5,opt,name=specifier,proto3" json:"specifier,omitempty"` + ClaimAuthority string `protobuf:"bytes,6,opt,name=claim_authority,json=claimAuthority,proto3" json:"claim_authority,omitempty"` +} + +func (x *RewardAttestationSignature) Reset() { + *x = RewardAttestationSignature{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[80] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RewardAttestationSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RewardAttestationSignature) ProtoMessage() {} + +func (x *RewardAttestationSignature) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[80] + 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 RewardAttestationSignature.ProtoReflect.Descriptor instead. +func (*RewardAttestationSignature) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{80} +} + +func (x *RewardAttestationSignature) GetEthRecipientAddress() string { + if x != nil { + return x.EthRecipientAddress + } + return "" +} + +func (x *RewardAttestationSignature) GetAmount() uint64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *RewardAttestationSignature) GetRewardAddress() string { + if x != nil { + return x.RewardAddress + } + return "" +} + +func (x *RewardAttestationSignature) GetRewardId() string { + if x != nil { + return x.RewardId + } + return "" +} + +func (x *RewardAttestationSignature) GetSpecifier() string { + if x != nil { + return x.Specifier + } + return "" +} + +func (x *RewardAttestationSignature) GetClaimAuthority() string { + if x != nil { + return x.ClaimAuthority + } + return "" +} + +type UploadSignature struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cid string `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` +} + +func (x *UploadSignature) Reset() { + *x = UploadSignature{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[81] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadSignature) ProtoMessage() {} + +func (x *UploadSignature) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[81] + 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 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{81} } func (x *UploadSignature) GetCid() string { @@ -4637,7 +5354,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[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4650,7 +5367,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[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4663,7 +5380,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{82} } func (x *FileUpload) GetUploaderAddress() string { @@ -4728,7 +5445,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[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4741,7 +5458,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[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4754,7 +5471,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{83} } func (x *GetStreamURLsSignature) GetAddresses() []string { @@ -4791,7 +5508,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[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4804,7 +5521,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[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4817,7 +5534,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{84} } func (x *GetStreamURLsRequest) GetSignature() string { @@ -4854,7 +5571,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[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4867,7 +5584,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[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4880,7 +5597,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{85} } func (x *GetStreamURLsResponse) GetEntityStreamUrls() map[string]*GetStreamURLsResponse_EntityStreamURLs { @@ -4901,7 +5618,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[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4914,7 +5631,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[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4927,7 +5644,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{86} } func (x *GetUploadByCIDRequest) GetCid() string { @@ -4951,7 +5668,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[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4964,7 +5681,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[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4977,7 +5694,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{87} } func (x *GetUploadByCIDResponse) GetExists() bool { @@ -5020,7 +5737,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[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5033,7 +5750,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[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5046,7 +5763,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{88} } func (x *GetRewardSenderAttestationRequest) GetAddress() string { @@ -5075,7 +5792,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[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5088,7 +5805,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[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5101,7 +5818,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{89} } func (x *GetRewardSenderAttestationResponse) GetOwner() string { @@ -5130,7 +5847,7 @@ type GetDeleteRewardSenderAttestationRequest struct { func (x *GetDeleteRewardSenderAttestationRequest) Reset() { *x = GetDeleteRewardSenderAttestationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[80] + mi := &file_core_v1_types_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5143,7 +5860,7 @@ func (x *GetDeleteRewardSenderAttestationRequest) String() string { func (*GetDeleteRewardSenderAttestationRequest) ProtoMessage() {} func (x *GetDeleteRewardSenderAttestationRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[80] + mi := &file_core_v1_types_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5156,7 +5873,7 @@ func (x *GetDeleteRewardSenderAttestationRequest) ProtoReflect() protoreflect.Me // Deprecated: Use GetDeleteRewardSenderAttestationRequest.ProtoReflect.Descriptor instead. func (*GetDeleteRewardSenderAttestationRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{80} + return file_core_v1_types_proto_rawDescGZIP(), []int{90} } func (x *GetDeleteRewardSenderAttestationRequest) GetAddress() string { @@ -5185,7 +5902,7 @@ type GetDeleteRewardSenderAttestationResponse struct { func (x *GetDeleteRewardSenderAttestationResponse) Reset() { *x = GetDeleteRewardSenderAttestationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[81] + mi := &file_core_v1_types_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5198,7 +5915,7 @@ func (x *GetDeleteRewardSenderAttestationResponse) String() string { func (*GetDeleteRewardSenderAttestationResponse) ProtoMessage() {} func (x *GetDeleteRewardSenderAttestationResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[81] + mi := &file_core_v1_types_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5211,7 +5928,7 @@ func (x *GetDeleteRewardSenderAttestationResponse) ProtoReflect() protoreflect.M // Deprecated: Use GetDeleteRewardSenderAttestationResponse.ProtoReflect.Descriptor instead. func (*GetDeleteRewardSenderAttestationResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{81} + return file_core_v1_types_proto_rawDescGZIP(), []int{91} } func (x *GetDeleteRewardSenderAttestationResponse) GetOwner() string { @@ -5239,7 +5956,7 @@ type StreamBlocksRequest struct { func (x *StreamBlocksRequest) Reset() { *x = StreamBlocksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[82] + mi := &file_core_v1_types_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5252,7 +5969,7 @@ func (x *StreamBlocksRequest) String() string { func (*StreamBlocksRequest) ProtoMessage() {} func (x *StreamBlocksRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[82] + mi := &file_core_v1_types_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5265,7 +5982,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{82} + return file_core_v1_types_proto_rawDescGZIP(), []int{92} } func (x *StreamBlocksRequest) GetCanon() bool { @@ -5286,7 +6003,7 @@ type StreamBlocksResponse struct { func (x *StreamBlocksResponse) Reset() { *x = StreamBlocksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[83] + mi := &file_core_v1_types_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5299,7 +6016,7 @@ func (x *StreamBlocksResponse) String() string { func (*StreamBlocksResponse) ProtoMessage() {} func (x *StreamBlocksResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[83] + mi := &file_core_v1_types_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5312,7 +6029,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{83} + return file_core_v1_types_proto_rawDescGZIP(), []int{93} } func (x *StreamBlocksResponse) GetBlock() *Block { @@ -5343,7 +6060,7 @@ type GetStatusResponse_ProcessInfo struct { func (x *GetStatusResponse_ProcessInfo) Reset() { *x = GetStatusResponse_ProcessInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[84] + mi := &file_core_v1_types_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5356,7 +6073,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[84] + mi := &file_core_v1_types_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5463,7 +6180,7 @@ type GetStatusResponse_NodeInfo struct { func (x *GetStatusResponse_NodeInfo) Reset() { *x = GetStatusResponse_NodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[85] + mi := &file_core_v1_types_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5476,7 +6193,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[85] + mi := &file_core_v1_types_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5534,7 +6251,7 @@ type GetStatusResponse_ChainInfo struct { func (x *GetStatusResponse_ChainInfo) Reset() { *x = GetStatusResponse_ChainInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[86] + mi := &file_core_v1_types_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5547,7 +6264,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[86] + mi := &file_core_v1_types_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5607,7 +6324,7 @@ type GetStatusResponse_SyncInfo struct { func (x *GetStatusResponse_SyncInfo) Reset() { *x = GetStatusResponse_SyncInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[87] + mi := &file_core_v1_types_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5620,7 +6337,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[87] + mi := &file_core_v1_types_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5698,7 +6415,7 @@ type GetStatusResponse_PruningInfo struct { func (x *GetStatusResponse_PruningInfo) Reset() { *x = GetStatusResponse_PruningInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[88] + mi := &file_core_v1_types_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5711,7 +6428,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[88] + mi := &file_core_v1_types_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5800,7 +6517,7 @@ type GetStatusResponse_ResourceInfo struct { func (x *GetStatusResponse_ResourceInfo) Reset() { *x = GetStatusResponse_ResourceInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[89] + mi := &file_core_v1_types_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5813,7 +6530,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[89] + mi := &file_core_v1_types_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5892,7 +6609,7 @@ type GetStatusResponse_MempoolInfo struct { func (x *GetStatusResponse_MempoolInfo) Reset() { *x = GetStatusResponse_MempoolInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[90] + mi := &file_core_v1_types_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5905,7 +6622,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[90] + mi := &file_core_v1_types_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5961,7 +6678,7 @@ type GetStatusResponse_SnapshotInfo struct { func (x *GetStatusResponse_SnapshotInfo) Reset() { *x = GetStatusResponse_SnapshotInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[91] + mi := &file_core_v1_types_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5974,7 +6691,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[91] + mi := &file_core_v1_types_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6015,7 +6732,7 @@ type GetStatusResponse_PeerInfo struct { func (x *GetStatusResponse_PeerInfo) Reset() { *x = GetStatusResponse_PeerInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[92] + mi := &file_core_v1_types_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6028,7 +6745,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[92] + mi := &file_core_v1_types_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6067,7 +6784,7 @@ type GetStatusResponse_StorageInfo struct { func (x *GetStatusResponse_StorageInfo) Reset() { *x = GetStatusResponse_StorageInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[93] + mi := &file_core_v1_types_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6080,7 +6797,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[93] + mi := &file_core_v1_types_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6153,7 +6870,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[94] + mi := &file_core_v1_types_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6166,7 +6883,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[94] + mi := &file_core_v1_types_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6233,7 +6950,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[95] + mi := &file_core_v1_types_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6246,7 +6963,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[95] + mi := &file_core_v1_types_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6320,7 +7037,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[96] + mi := &file_core_v1_types_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6333,7 +7050,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[96] + mi := &file_core_v1_types_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6412,7 +7129,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[97] + mi := &file_core_v1_types_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6425,7 +7142,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[97] + mi := &file_core_v1_types_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6536,7 +7253,7 @@ type GetStreamURLsResponse_EntityStreamURLs struct { func (x *GetStreamURLsResponse_EntityStreamURLs) Reset() { *x = GetStreamURLsResponse_EntityStreamURLs{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[99] + mi := &file_core_v1_types_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6549,7 +7266,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[99] + mi := &file_core_v1_types_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6562,7 +7279,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{85, 0} } func (x *GetStreamURLsResponse_EntityStreamURLs) GetEntityType() string { @@ -7082,7 +7799,7 @@ var file_core_v1_types_proto_rawDesc = []byte{ 0x73, 0x61, 0x63, 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, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x32, 0x22, 0x9b, 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, @@ -7135,7 +7852,11 @@ var file_core_v1_types_proto_rawDesc = []byte{ 0x37, 0x0a, 0x0b, 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, + 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x77, 0x61, + 0x72, 0x64, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0xf3, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, + 0x6f, 0x6f, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 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, @@ -7410,183 +8131,269 @@ var file_core_v1_types_proto_rawDesc = []byte{ 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, + 0x03, 0x70, 0x69, 0x65, 0x22, 0x56, 0x0a, 0x0d, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xae, 0x01, 0x0a, + 0x0a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x42, 0x6f, 0x64, 0x79, 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, 0x01, 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, + 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, 0xd4, 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, 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, 0x07, 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, 0x4a, 0x04, 0x08, + 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x52, 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, 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, + 0x65, 0x73, 0x52, 0x15, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0x56, 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, 0x4a, 0x04, + 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x15, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x8c, 0x01, 0x0a, + 0x11, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2c, 0x0a, + 0x12, 0x72, 0x6d, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x72, 0x6d, 0x4f, 0x77, 0x6e, + 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xd3, 0x01, 0x0a, 0x0e, + 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x42, 0x6f, 0x64, 0x79, 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, 0x01, 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, 0x34, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0xd0, 0x0f, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x00, + 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x4d, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0xd1, 0x0f, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x69, 0x65, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x6a, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x50, 0x6f, 0x6f, 0x6c, 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, + 0x01, 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, 0x12, 0x20, 0x0a, 0x0b, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x72, 0x0a, + 0x18, 0x53, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 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, 0x01, 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, 0x12, + 0x20, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x13, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x52, 0x65, 0x77, 0x61, + 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x06, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x12, 0x36, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x67, + 0x61, 0x63, 0x79, 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, 0xf5, 0x01, 0x0a, 0x12, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 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, 0x80, 0x01, 0x0a, 0x12, + 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 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, 0x4c, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x01, 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, 0x6f, 0x0a, 0x15, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, + 0x01, 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, 0x12, 0x20, 0x0a, 0x0b, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 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, 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, + 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, 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, 0x79, 0x0a, 0x27, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 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, - 0x79, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 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, 0x62, 0x0a, 0x28, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 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, + 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, 0x62, 0x0a, 0x28, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 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 ( @@ -7602,7 +8409,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, 101) +var file_core_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 111) 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 @@ -7672,81 +8479,91 @@ var file_core_v1_types_proto_goTypes = []interface{}{ (*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 - (*GetDeleteRewardSenderAttestationRequest)(nil), // 82: core.v1.GetDeleteRewardSenderAttestationRequest - (*GetDeleteRewardSenderAttestationResponse)(nil), // 83: core.v1.GetDeleteRewardSenderAttestationResponse - (*StreamBlocksRequest)(nil), // 84: core.v1.StreamBlocksRequest - (*StreamBlocksResponse)(nil), // 85: core.v1.StreamBlocksResponse - (*GetStatusResponse_ProcessInfo)(nil), // 86: core.v1.GetStatusResponse.ProcessInfo - (*GetStatusResponse_NodeInfo)(nil), // 87: core.v1.GetStatusResponse.NodeInfo - (*GetStatusResponse_ChainInfo)(nil), // 88: core.v1.GetStatusResponse.ChainInfo - (*GetStatusResponse_SyncInfo)(nil), // 89: core.v1.GetStatusResponse.SyncInfo - (*GetStatusResponse_PruningInfo)(nil), // 90: core.v1.GetStatusResponse.PruningInfo - (*GetStatusResponse_ResourceInfo)(nil), // 91: core.v1.GetStatusResponse.ResourceInfo - (*GetStatusResponse_MempoolInfo)(nil), // 92: core.v1.GetStatusResponse.MempoolInfo - (*GetStatusResponse_SnapshotInfo)(nil), // 93: core.v1.GetStatusResponse.SnapshotInfo - (*GetStatusResponse_PeerInfo)(nil), // 94: core.v1.GetStatusResponse.PeerInfo - (*GetStatusResponse_StorageInfo)(nil), // 95: core.v1.GetStatusResponse.StorageInfo - (*GetStatusResponse_ProcessInfo_ProcessStateInfo)(nil), // 96: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - (*GetStatusResponse_SyncInfo_StateSyncInfo)(nil), // 97: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo - (*GetStatusResponse_SyncInfo_BlockSyncInfo)(nil), // 98: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo - (*GetStatusResponse_PeerInfo_Peer)(nil), // 99: core.v1.GetStatusResponse.PeerInfo.Peer - nil, // 100: core.v1.GetBlocksResponse.BlocksEntry - (*GetStreamURLsResponse_EntityStreamURLs)(nil), // 101: core.v1.GetStreamURLsResponse.EntityStreamURLs - nil, // 102: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry - (*v1beta1.Transaction)(nil), // 103: core.v1beta1.Transaction - (*v1beta1.TransactionReceipt)(nil), // 104: core.v1beta1.TransactionReceipt - (*timestamppb.Timestamp)(nil), // 105: google.protobuf.Timestamp - (*v1beta11.NewReleaseMessage)(nil), // 106: ddex.v1beta1.NewReleaseMessage - (*v1beta11.Party)(nil), // 107: ddex.v1beta1.Party - (*v1beta11.Resource)(nil), // 108: ddex.v1beta1.Resource - (*v1beta11.Release)(nil), // 109: ddex.v1beta1.Release - (*v1beta11.Deal)(nil), // 110: ddex.v1beta1.Deal - (*v1beta11.MeadMessage)(nil), // 111: ddex.v1beta1.MeadMessage - (*v1beta11.PieMessage)(nil), // 112: ddex.v1beta1.PieMessage + (*RewardBody)(nil), // 68: core.v1.RewardBody + (*CreateReward)(nil), // 69: core.v1.CreateReward + (*DeleteReward)(nil), // 70: core.v1.DeleteReward + (*RewardPoolMessage)(nil), // 71: core.v1.RewardPoolMessage + (*RewardPoolBody)(nil), // 72: core.v1.RewardPoolBody + (*CreateRewardPool)(nil), // 73: core.v1.CreateRewardPool + (*SetRewardPoolAuthorities)(nil), // 74: core.v1.SetRewardPoolAuthorities + (*LegacyRewardMessage)(nil), // 75: core.v1.LegacyRewardMessage + (*LegacyCreateReward)(nil), // 76: core.v1.LegacyCreateReward + (*LegacyDeleteReward)(nil), // 77: core.v1.LegacyDeleteReward + (*GetRewardPoolRequest)(nil), // 78: core.v1.GetRewardPoolRequest + (*GetRewardPoolResponse)(nil), // 79: core.v1.GetRewardPoolResponse + (*GetRewardRequest)(nil), // 80: core.v1.GetRewardRequest + (*GetRewardResponse)(nil), // 81: core.v1.GetRewardResponse + (*RewardAttestationSignature)(nil), // 82: core.v1.RewardAttestationSignature + (*UploadSignature)(nil), // 83: core.v1.UploadSignature + (*FileUpload)(nil), // 84: core.v1.FileUpload + (*GetStreamURLsSignature)(nil), // 85: core.v1.GetStreamURLsSignature + (*GetStreamURLsRequest)(nil), // 86: core.v1.GetStreamURLsRequest + (*GetStreamURLsResponse)(nil), // 87: core.v1.GetStreamURLsResponse + (*GetUploadByCIDRequest)(nil), // 88: core.v1.GetUploadByCIDRequest + (*GetUploadByCIDResponse)(nil), // 89: core.v1.GetUploadByCIDResponse + (*GetRewardSenderAttestationRequest)(nil), // 90: core.v1.GetRewardSenderAttestationRequest + (*GetRewardSenderAttestationResponse)(nil), // 91: core.v1.GetRewardSenderAttestationResponse + (*GetDeleteRewardSenderAttestationRequest)(nil), // 92: core.v1.GetDeleteRewardSenderAttestationRequest + (*GetDeleteRewardSenderAttestationResponse)(nil), // 93: core.v1.GetDeleteRewardSenderAttestationResponse + (*StreamBlocksRequest)(nil), // 94: core.v1.StreamBlocksRequest + (*StreamBlocksResponse)(nil), // 95: core.v1.StreamBlocksResponse + (*GetStatusResponse_ProcessInfo)(nil), // 96: core.v1.GetStatusResponse.ProcessInfo + (*GetStatusResponse_NodeInfo)(nil), // 97: core.v1.GetStatusResponse.NodeInfo + (*GetStatusResponse_ChainInfo)(nil), // 98: core.v1.GetStatusResponse.ChainInfo + (*GetStatusResponse_SyncInfo)(nil), // 99: core.v1.GetStatusResponse.SyncInfo + (*GetStatusResponse_PruningInfo)(nil), // 100: core.v1.GetStatusResponse.PruningInfo + (*GetStatusResponse_ResourceInfo)(nil), // 101: core.v1.GetStatusResponse.ResourceInfo + (*GetStatusResponse_MempoolInfo)(nil), // 102: core.v1.GetStatusResponse.MempoolInfo + (*GetStatusResponse_SnapshotInfo)(nil), // 103: core.v1.GetStatusResponse.SnapshotInfo + (*GetStatusResponse_PeerInfo)(nil), // 104: core.v1.GetStatusResponse.PeerInfo + (*GetStatusResponse_StorageInfo)(nil), // 105: core.v1.GetStatusResponse.StorageInfo + (*GetStatusResponse_ProcessInfo_ProcessStateInfo)(nil), // 106: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + (*GetStatusResponse_SyncInfo_StateSyncInfo)(nil), // 107: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo + (*GetStatusResponse_SyncInfo_BlockSyncInfo)(nil), // 108: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo + (*GetStatusResponse_PeerInfo_Peer)(nil), // 109: core.v1.GetStatusResponse.PeerInfo.Peer + nil, // 110: core.v1.GetBlocksResponse.BlocksEntry + (*GetStreamURLsResponse_EntityStreamURLs)(nil), // 111: core.v1.GetStreamURLsResponse.EntityStreamURLs + nil, // 112: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry + (*v1beta1.Transaction)(nil), // 113: core.v1beta1.Transaction + (*v1beta1.TransactionReceipt)(nil), // 114: core.v1beta1.TransactionReceipt + (*timestamppb.Timestamp)(nil), // 115: google.protobuf.Timestamp + (*v1beta11.NewReleaseMessage)(nil), // 116: ddex.v1beta1.NewReleaseMessage + (*v1beta11.Party)(nil), // 117: ddex.v1beta1.Party + (*v1beta11.Resource)(nil), // 118: ddex.v1beta1.Resource + (*v1beta11.Release)(nil), // 119: ddex.v1beta1.Release + (*v1beta11.Deal)(nil), // 120: ddex.v1beta1.Deal + (*v1beta11.MeadMessage)(nil), // 121: ddex.v1beta1.MeadMessage + (*v1beta11.PieMessage)(nil), // 122: ddex.v1beta1.PieMessage } var file_core_v1_types_proto_depIdxs = []int32{ - 87, // 0: core.v1.GetStatusResponse.node_info:type_name -> core.v1.GetStatusResponse.NodeInfo - 88, // 1: core.v1.GetStatusResponse.chain_info:type_name -> core.v1.GetStatusResponse.ChainInfo - 89, // 2: core.v1.GetStatusResponse.sync_info:type_name -> core.v1.GetStatusResponse.SyncInfo - 90, // 3: core.v1.GetStatusResponse.pruning_info:type_name -> core.v1.GetStatusResponse.PruningInfo - 91, // 4: core.v1.GetStatusResponse.resource_info:type_name -> core.v1.GetStatusResponse.ResourceInfo - 92, // 5: core.v1.GetStatusResponse.mempool_info:type_name -> core.v1.GetStatusResponse.MempoolInfo - 94, // 6: core.v1.GetStatusResponse.peers:type_name -> core.v1.GetStatusResponse.PeerInfo - 93, // 7: core.v1.GetStatusResponse.snapshot_info:type_name -> core.v1.GetStatusResponse.SnapshotInfo - 86, // 8: core.v1.GetStatusResponse.process_info:type_name -> core.v1.GetStatusResponse.ProcessInfo - 95, // 9: core.v1.GetStatusResponse.storage_info:type_name -> core.v1.GetStatusResponse.StorageInfo + 97, // 0: core.v1.GetStatusResponse.node_info:type_name -> core.v1.GetStatusResponse.NodeInfo + 98, // 1: core.v1.GetStatusResponse.chain_info:type_name -> core.v1.GetStatusResponse.ChainInfo + 99, // 2: core.v1.GetStatusResponse.sync_info:type_name -> core.v1.GetStatusResponse.SyncInfo + 100, // 3: core.v1.GetStatusResponse.pruning_info:type_name -> core.v1.GetStatusResponse.PruningInfo + 101, // 4: core.v1.GetStatusResponse.resource_info:type_name -> core.v1.GetStatusResponse.ResourceInfo + 102, // 5: core.v1.GetStatusResponse.mempool_info:type_name -> core.v1.GetStatusResponse.MempoolInfo + 104, // 6: core.v1.GetStatusResponse.peers:type_name -> core.v1.GetStatusResponse.PeerInfo + 103, // 7: core.v1.GetStatusResponse.snapshot_info:type_name -> core.v1.GetStatusResponse.SnapshotInfo + 96, // 8: core.v1.GetStatusResponse.process_info:type_name -> core.v1.GetStatusResponse.ProcessInfo + 105, // 9: core.v1.GetStatusResponse.storage_info:type_name -> core.v1.GetStatusResponse.StorageInfo 24, // 10: core.v1.GetBlockResponse.block:type_name -> core.v1.Block - 100, // 11: core.v1.GetBlocksResponse.blocks:type_name -> core.v1.GetBlocksResponse.BlocksEntry + 110, // 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 - 103, // 14: core.v1.SendTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction + 113, // 14: core.v1.SendTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction 25, // 15: core.v1.SendTransactionResponse.transaction:type_name -> core.v1.Transaction - 104, // 16: core.v1.SendTransactionResponse.transaction_receipt:type_name -> core.v1beta1.TransactionReceipt + 114, // 16: core.v1.SendTransactionResponse.transaction_receipt:type_name -> core.v1beta1.TransactionReceipt 26, // 17: core.v1.ForwardTransactionRequest.transaction:type_name -> core.v1.SignedTransaction - 103, // 18: core.v1.ForwardTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction + 113, // 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 - 105, // 23: core.v1.Block.timestamp:type_name -> google.protobuf.Timestamp + 115, // 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 - 105, // 26: core.v1.Transaction.timestamp:type_name -> google.protobuf.Timestamp - 103, // 27: core.v1.Transaction.transactionv2:type_name -> core.v1beta1.Transaction + 115, // 26: core.v1.Transaction.timestamp:type_name -> google.protobuf.Timestamp + 113, // 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 @@ -7755,69 +8572,76 @@ var file_core_v1_types_proto_depIdxs = []int32{ 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 - 106, // 36: core.v1.SignedTransaction.release:type_name -> ddex.v1beta1.NewReleaseMessage + 116, // 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 - 105, // 40: core.v1.TrackPlay.timestamp:type_name -> google.protobuf.Timestamp - 105, // 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 - 105, // 48: core.v1.SlashRecommendation.start:type_name -> google.protobuf.Timestamp - 105, // 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 - 106, // 53: core.v1.GetERNResponse.ern:type_name -> ddex.v1beta1.NewReleaseMessage - 107, // 54: core.v1.GetPartyResponse.party:type_name -> ddex.v1beta1.Party - 108, // 55: core.v1.GetResourceResponse.resource:type_name -> ddex.v1beta1.Resource - 109, // 56: core.v1.GetReleaseResponse.release:type_name -> ddex.v1beta1.Release - 110, // 57: core.v1.GetDealResponse.deal:type_name -> ddex.v1beta1.Deal - 111, // 58: core.v1.GetMEADResponse.mead:type_name -> ddex.v1beta1.MeadMessage - 112, // 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 - 105, // 63: core.v1.GetStreamURLsSignature.expires_at:type_name -> google.protobuf.Timestamp - 105, // 64: core.v1.GetStreamURLsRequest.expires_at:type_name -> google.protobuf.Timestamp - 102, // 65: core.v1.GetStreamURLsResponse.entity_stream_urls:type_name -> core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry - 24, // 66: core.v1.StreamBlocksResponse.block:type_name -> core.v1.Block - 96, // 67: core.v1.GetStatusResponse.ProcessInfo.abci:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 68: core.v1.GetStatusResponse.ProcessInfo.registry_bridge:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 69: core.v1.GetStatusResponse.ProcessInfo.echo_server:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 70: core.v1.GetStatusResponse.ProcessInfo.sync_tasks:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 71: core.v1.GetStatusResponse.ProcessInfo.peer_manager:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 72: core.v1.GetStatusResponse.ProcessInfo.data_companion:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 73: core.v1.GetStatusResponse.ProcessInfo.cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 74: core.v1.GetStatusResponse.ProcessInfo.log_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 75: core.v1.GetStatusResponse.ProcessInfo.state_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 76: core.v1.GetStatusResponse.ProcessInfo.mempool_cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 96, // 77: core.v1.GetStatusResponse.ProcessInfo.restore:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 97, // 78: core.v1.GetStatusResponse.SyncInfo.state_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo - 98, // 79: core.v1.GetStatusResponse.SyncInfo.block_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo - 41, // 80: core.v1.GetStatusResponse.SnapshotInfo.snapshots:type_name -> core.v1.SnapshotMetadata - 99, // 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 - 105, // 83: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.started_at:type_name -> google.protobuf.Timestamp - 105, // 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 - 41, // 86: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.snapshot:type_name -> core.v1.SnapshotMetadata - 105, // 87: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.started_at:type_name -> google.protobuf.Timestamp - 105, // 88: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.completed_at:type_name -> google.protobuf.Timestamp - 87, // 89: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.head_source:type_name -> core.v1.GetStatusResponse.NodeInfo - 105, // 90: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.started_at:type_name -> google.protobuf.Timestamp - 105, // 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 - 101, // 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 + 84, // 38: core.v1.SignedTransaction.file_upload:type_name -> core.v1.FileUpload + 71, // 39: core.v1.SignedTransaction.reward_pool:type_name -> core.v1.RewardPoolMessage + 29, // 40: core.v1.TrackPlays.plays:type_name -> core.v1.TrackPlay + 115, // 41: core.v1.TrackPlay.timestamp:type_name -> google.protobuf.Timestamp + 115, // 42: core.v1.SlaRollup.timestamp:type_name -> google.protobuf.Timestamp + 31, // 43: core.v1.SlaRollup.reports:type_name -> core.v1.SlaNodeReport + 37, // 44: core.v1.Attestation.validator_registration:type_name -> core.v1.ValidatorRegistration + 38, // 45: core.v1.Attestation.validator_deregistration:type_name -> core.v1.ValidatorDeregistration + 41, // 46: core.v1.GetStoredSnapshotsResponse.snapshots:type_name -> core.v1.SnapshotMetadata + 42, // 47: core.v1.Reward.claim_authorities:type_name -> core.v1.ClaimAuthority + 81, // 48: core.v1.GetRewardsResponse.rewards:type_name -> core.v1.GetRewardResponse + 115, // 49: core.v1.SlashRecommendation.start:type_name -> google.protobuf.Timestamp + 115, // 50: core.v1.SlashRecommendation.end:type_name -> google.protobuf.Timestamp + 48, // 51: core.v1.GetSlashAttestationRequest.data:type_name -> core.v1.SlashRecommendation + 49, // 52: core.v1.GetSlashAttestationsRequest.request:type_name -> core.v1.GetSlashAttestationRequest + 50, // 53: core.v1.GetSlashAttestationsResponse.attestations:type_name -> core.v1.GetSlashAttestationResponse + 116, // 54: core.v1.GetERNResponse.ern:type_name -> ddex.v1beta1.NewReleaseMessage + 117, // 55: core.v1.GetPartyResponse.party:type_name -> ddex.v1beta1.Party + 118, // 56: core.v1.GetResourceResponse.resource:type_name -> ddex.v1beta1.Resource + 119, // 57: core.v1.GetReleaseResponse.release:type_name -> ddex.v1beta1.Release + 120, // 58: core.v1.GetDealResponse.deal:type_name -> ddex.v1beta1.Deal + 121, // 59: core.v1.GetMEADResponse.mead:type_name -> ddex.v1beta1.MeadMessage + 122, // 60: core.v1.GetPIEResponse.pie:type_name -> ddex.v1beta1.PieMessage + 68, // 61: core.v1.RewardMessage.body:type_name -> core.v1.RewardBody + 69, // 62: core.v1.RewardBody.create:type_name -> core.v1.CreateReward + 70, // 63: core.v1.RewardBody.delete:type_name -> core.v1.DeleteReward + 72, // 64: core.v1.RewardPoolMessage.body:type_name -> core.v1.RewardPoolBody + 73, // 65: core.v1.RewardPoolBody.create:type_name -> core.v1.CreateRewardPool + 74, // 66: core.v1.RewardPoolBody.set_authorities:type_name -> core.v1.SetRewardPoolAuthorities + 76, // 67: core.v1.LegacyRewardMessage.create:type_name -> core.v1.LegacyCreateReward + 77, // 68: core.v1.LegacyRewardMessage.delete:type_name -> core.v1.LegacyDeleteReward + 42, // 69: core.v1.LegacyCreateReward.claim_authorities:type_name -> core.v1.ClaimAuthority + 115, // 70: core.v1.GetStreamURLsSignature.expires_at:type_name -> google.protobuf.Timestamp + 115, // 71: core.v1.GetStreamURLsRequest.expires_at:type_name -> google.protobuf.Timestamp + 112, // 72: core.v1.GetStreamURLsResponse.entity_stream_urls:type_name -> core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry + 24, // 73: core.v1.StreamBlocksResponse.block:type_name -> core.v1.Block + 106, // 74: core.v1.GetStatusResponse.ProcessInfo.abci:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 75: core.v1.GetStatusResponse.ProcessInfo.registry_bridge:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 76: core.v1.GetStatusResponse.ProcessInfo.echo_server:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 77: core.v1.GetStatusResponse.ProcessInfo.sync_tasks:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 78: core.v1.GetStatusResponse.ProcessInfo.peer_manager:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 79: core.v1.GetStatusResponse.ProcessInfo.data_companion:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 80: core.v1.GetStatusResponse.ProcessInfo.cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 81: core.v1.GetStatusResponse.ProcessInfo.log_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 82: core.v1.GetStatusResponse.ProcessInfo.state_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 83: core.v1.GetStatusResponse.ProcessInfo.mempool_cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 106, // 84: core.v1.GetStatusResponse.ProcessInfo.restore:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 107, // 85: core.v1.GetStatusResponse.SyncInfo.state_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo + 108, // 86: core.v1.GetStatusResponse.SyncInfo.block_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo + 41, // 87: core.v1.GetStatusResponse.SnapshotInfo.snapshots:type_name -> core.v1.SnapshotMetadata + 109, // 88: core.v1.GetStatusResponse.PeerInfo.peers:type_name -> core.v1.GetStatusResponse.PeerInfo.Peer + 0, // 89: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.state:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessState + 115, // 90: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.started_at:type_name -> google.protobuf.Timestamp + 115, // 91: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.completed_at:type_name -> google.protobuf.Timestamp + 1, // 92: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.phase:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.Phase + 41, // 93: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.snapshot:type_name -> core.v1.SnapshotMetadata + 115, // 94: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.started_at:type_name -> google.protobuf.Timestamp + 115, // 95: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.completed_at:type_name -> google.protobuf.Timestamp + 97, // 96: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.head_source:type_name -> core.v1.GetStatusResponse.NodeInfo + 115, // 97: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.started_at:type_name -> google.protobuf.Timestamp + 115, // 98: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.completed_at:type_name -> google.protobuf.Timestamp + 24, // 99: core.v1.GetBlocksResponse.BlocksEntry.value:type_name -> core.v1.Block + 111, // 100: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry.value:type_name -> core.v1.GetStreamURLsResponse.EntityStreamURLs + 101, // [101:101] is the sub-list for method output_type + 101, // [101:101] is the sub-list for method input_type + 101, // [101:101] is the sub-list for extension type_name + 101, // [101:101] is the sub-list for extension extendee + 0, // [0:101] is the sub-list for field type_name } func init() { file_core_v1_types_proto_init() } @@ -8619,7 +9443,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.(*RewardBody); i { case 0: return &v.state case 1: @@ -8631,7 +9455,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: @@ -8643,7 +9467,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: @@ -8655,7 +9479,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.(*RewardPoolMessage); i { case 0: return &v.state case 1: @@ -8667,7 +9491,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.(*RewardPoolBody); i { case 0: return &v.state case 1: @@ -8679,7 +9503,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.(*CreateRewardPool); i { case 0: return &v.state case 1: @@ -8691,7 +9515,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.(*SetRewardPoolAuthorities); i { case 0: return &v.state case 1: @@ -8703,7 +9527,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.(*LegacyRewardMessage); i { case 0: return &v.state case 1: @@ -8715,7 +9539,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.(*LegacyCreateReward); i { case 0: return &v.state case 1: @@ -8727,7 +9551,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.(*LegacyDeleteReward); i { case 0: return &v.state case 1: @@ -8739,7 +9563,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.(*GetRewardPoolRequest); i { case 0: return &v.state case 1: @@ -8751,7 +9575,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.(*GetRewardPoolResponse); i { case 0: return &v.state case 1: @@ -8763,7 +9587,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.(*GetRewardRequest); i { case 0: return &v.state case 1: @@ -8775,7 +9599,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.(*GetRewardResponse); i { case 0: return &v.state case 1: @@ -8787,7 +9611,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.(*GetDeleteRewardSenderAttestationRequest); i { + switch v := v.(*RewardAttestationSignature); i { case 0: return &v.state case 1: @@ -8799,7 +9623,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.(*GetDeleteRewardSenderAttestationResponse); i { + switch v := v.(*UploadSignature); i { case 0: return &v.state case 1: @@ -8811,7 +9635,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.(*StreamBlocksRequest); i { + switch v := v.(*FileUpload); i { case 0: return &v.state case 1: @@ -8823,7 +9647,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.(*StreamBlocksResponse); i { + switch v := v.(*GetStreamURLsSignature); i { case 0: return &v.state case 1: @@ -8835,7 +9659,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_ProcessInfo); i { + switch v := v.(*GetStreamURLsRequest); i { case 0: return &v.state case 1: @@ -8847,7 +9671,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_NodeInfo); i { + switch v := v.(*GetStreamURLsResponse); i { case 0: return &v.state case 1: @@ -8859,7 +9683,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_ChainInfo); i { + switch v := v.(*GetUploadByCIDRequest); i { case 0: return &v.state case 1: @@ -8871,7 +9695,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_SyncInfo); i { + switch v := v.(*GetUploadByCIDResponse); i { case 0: return &v.state case 1: @@ -8883,7 +9707,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_PruningInfo); i { + switch v := v.(*GetRewardSenderAttestationRequest); i { case 0: return &v.state case 1: @@ -8895,7 +9719,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_ResourceInfo); i { + switch v := v.(*GetRewardSenderAttestationResponse); i { case 0: return &v.state case 1: @@ -8907,7 +9731,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_MempoolInfo); i { + switch v := v.(*GetDeleteRewardSenderAttestationRequest); i { case 0: return &v.state case 1: @@ -8919,7 +9743,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_SnapshotInfo); i { + switch v := v.(*GetDeleteRewardSenderAttestationResponse); i { case 0: return &v.state case 1: @@ -8931,7 +9755,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_PeerInfo); i { + switch v := v.(*StreamBlocksRequest); i { case 0: return &v.state case 1: @@ -8943,7 +9767,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_StorageInfo); i { + switch v := v.(*StreamBlocksResponse); i { case 0: return &v.state case 1: @@ -8955,7 +9779,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_ProcessInfo_ProcessStateInfo); i { + switch v := v.(*GetStatusResponse_ProcessInfo); i { case 0: return &v.state case 1: @@ -8967,7 +9791,7 @@ 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_StateSyncInfo); i { + switch v := v.(*GetStatusResponse_NodeInfo); i { case 0: return &v.state case 1: @@ -8979,7 +9803,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_SyncInfo_BlockSyncInfo); i { + switch v := v.(*GetStatusResponse_ChainInfo); i { case 0: return &v.state case 1: @@ -8991,7 +9815,19 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_PeerInfo_Peer); i { + switch v := v.(*GetStatusResponse_SyncInfo); 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[98].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_PruningInfo); i { case 0: return &v.state case 1: @@ -9003,6 +9839,114 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_ResourceInfo); 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[100].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_MempoolInfo); 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[101].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_SnapshotInfo); 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[102].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_PeerInfo); 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[103].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_StorageInfo); 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[104].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_ProcessInfo_ProcessStateInfo); 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[105].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_SyncInfo_StateSyncInfo); 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[106].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[107].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_PeerInfo_Peer); 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[109].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetStreamURLsResponse_EntityStreamURLs); i { case 0: return &v.state @@ -9027,16 +9971,25 @@ func file_core_v1_types_proto_init() { (*SignedTransaction_Release)(nil), (*SignedTransaction_Reward)(nil), (*SignedTransaction_FileUpload)(nil), + (*SignedTransaction_RewardPool)(nil), } file_core_v1_types_proto_msgTypes[34].OneofWrappers = []interface{}{ (*Attestation_ValidatorRegistration)(nil), (*Attestation_ValidatorDeregistration)(nil), } - file_core_v1_types_proto_msgTypes[65].OneofWrappers = []interface{}{ - (*RewardMessage_Create)(nil), - (*RewardMessage_Delete)(nil), + file_core_v1_types_proto_msgTypes[66].OneofWrappers = []interface{}{ + (*RewardBody_Create)(nil), + (*RewardBody_Delete)(nil), + } + file_core_v1_types_proto_msgTypes[70].OneofWrappers = []interface{}{ + (*RewardPoolBody_Create)(nil), + (*RewardPoolBody_SetAuthorities)(nil), + } + file_core_v1_types_proto_msgTypes[73].OneofWrappers = []interface{}{ + (*LegacyRewardMessage_Create)(nil), + (*LegacyRewardMessage_Delete)(nil), } - file_core_v1_types_proto_msgTypes[87].OneofWrappers = []interface{}{ + file_core_v1_types_proto_msgTypes[97].OneofWrappers = []interface{}{ (*GetStatusResponse_SyncInfo_StateSync)(nil), (*GetStatusResponse_SyncInfo_BlockSync)(nil), } @@ -9046,7 +9999,7 @@ func file_core_v1_types_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_v1_types_proto_rawDesc, NumEnums: 2, - NumMessages: 101, + NumMessages: 111, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/api/core/v1/v1connect/service.connect.go b/pkg/api/core/v1/v1connect/service.connect.go index 52dbcffe..80f1787f 100644 --- a/pkg/api/core/v1/v1connect/service.connect.go +++ b/pkg/api/core/v1/v1connect/service.connect.go @@ -79,6 +79,9 @@ const ( CoreServiceGetRewardProcedure = "/core.v1.CoreService/GetReward" // CoreServiceGetRewardsProcedure is the fully-qualified name of the CoreService's GetRewards RPC. CoreServiceGetRewardsProcedure = "/core.v1.CoreService/GetRewards" + // CoreServiceGetRewardPoolProcedure is the fully-qualified name of the CoreService's GetRewardPool + // RPC. + CoreServiceGetRewardPoolProcedure = "/core.v1.CoreService/GetRewardPool" // CoreServiceGetRewardAttestationProcedure is the fully-qualified name of the CoreService's // GetRewardAttestation RPC. CoreServiceGetRewardAttestationProcedure = "/core.v1.CoreService/GetRewardAttestation" @@ -120,6 +123,7 @@ type CoreServiceClient interface { GetPIE(context.Context, *connect.Request[v1.GetPIERequest]) (*connect.Response[v1.GetPIEResponse], error) GetReward(context.Context, *connect.Request[v1.GetRewardRequest]) (*connect.Response[v1.GetRewardResponse], error) GetRewards(context.Context, *connect.Request[v1.GetRewardsRequest]) (*connect.Response[v1.GetRewardsResponse], error) + GetRewardPool(context.Context, *connect.Request[v1.GetRewardPoolRequest]) (*connect.Response[v1.GetRewardPoolResponse], error) GetRewardAttestation(context.Context, *connect.Request[v1.GetRewardAttestationRequest]) (*connect.Response[v1.GetRewardAttestationResponse], error) GetRewardSenderAttestation(context.Context, *connect.Request[v1.GetRewardSenderAttestationRequest]) (*connect.Response[v1.GetRewardSenderAttestationResponse], error) GetDeleteRewardSenderAttestation(context.Context, *connect.Request[v1.GetDeleteRewardSenderAttestationRequest]) (*connect.Response[v1.GetDeleteRewardSenderAttestationResponse], error) @@ -253,6 +257,12 @@ func NewCoreServiceClient(httpClient connect.HTTPClient, baseURL string, opts .. connect.WithSchema(coreServiceMethods.ByName("GetRewards")), connect.WithClientOptions(opts...), ), + getRewardPool: connect.NewClient[v1.GetRewardPoolRequest, v1.GetRewardPoolResponse]( + httpClient, + baseURL+CoreServiceGetRewardPoolProcedure, + connect.WithSchema(coreServiceMethods.ByName("GetRewardPool")), + connect.WithClientOptions(opts...), + ), getRewardAttestation: connect.NewClient[v1.GetRewardAttestationRequest, v1.GetRewardAttestationResponse]( httpClient, baseURL+CoreServiceGetRewardAttestationProcedure, @@ -313,6 +323,7 @@ type coreServiceClient struct { getPIE *connect.Client[v1.GetPIERequest, v1.GetPIEResponse] getReward *connect.Client[v1.GetRewardRequest, v1.GetRewardResponse] getRewards *connect.Client[v1.GetRewardsRequest, v1.GetRewardsResponse] + getRewardPool *connect.Client[v1.GetRewardPoolRequest, v1.GetRewardPoolResponse] getRewardAttestation *connect.Client[v1.GetRewardAttestationRequest, v1.GetRewardAttestationResponse] getRewardSenderAttestation *connect.Client[v1.GetRewardSenderAttestationRequest, v1.GetRewardSenderAttestationResponse] getDeleteRewardSenderAttestation *connect.Client[v1.GetDeleteRewardSenderAttestationRequest, v1.GetDeleteRewardSenderAttestationResponse] @@ -416,6 +427,11 @@ func (c *coreServiceClient) GetRewards(ctx context.Context, req *connect.Request return c.getRewards.CallUnary(ctx, req) } +// GetRewardPool calls core.v1.CoreService.GetRewardPool. +func (c *coreServiceClient) GetRewardPool(ctx context.Context, req *connect.Request[v1.GetRewardPoolRequest]) (*connect.Response[v1.GetRewardPoolResponse], error) { + return c.getRewardPool.CallUnary(ctx, req) +} + // GetRewardAttestation calls core.v1.CoreService.GetRewardAttestation. func (c *coreServiceClient) GetRewardAttestation(ctx context.Context, req *connect.Request[v1.GetRewardAttestationRequest]) (*connect.Response[v1.GetRewardAttestationResponse], error) { return c.getRewardAttestation.CallUnary(ctx, req) @@ -467,6 +483,7 @@ type CoreServiceHandler interface { GetPIE(context.Context, *connect.Request[v1.GetPIERequest]) (*connect.Response[v1.GetPIEResponse], error) GetReward(context.Context, *connect.Request[v1.GetRewardRequest]) (*connect.Response[v1.GetRewardResponse], error) GetRewards(context.Context, *connect.Request[v1.GetRewardsRequest]) (*connect.Response[v1.GetRewardsResponse], error) + GetRewardPool(context.Context, *connect.Request[v1.GetRewardPoolRequest]) (*connect.Response[v1.GetRewardPoolResponse], error) GetRewardAttestation(context.Context, *connect.Request[v1.GetRewardAttestationRequest]) (*connect.Response[v1.GetRewardAttestationResponse], error) GetRewardSenderAttestation(context.Context, *connect.Request[v1.GetRewardSenderAttestationRequest]) (*connect.Response[v1.GetRewardSenderAttestationResponse], error) GetDeleteRewardSenderAttestation(context.Context, *connect.Request[v1.GetDeleteRewardSenderAttestationRequest]) (*connect.Response[v1.GetDeleteRewardSenderAttestationResponse], error) @@ -596,6 +613,12 @@ func NewCoreServiceHandler(svc CoreServiceHandler, opts ...connect.HandlerOption connect.WithSchema(coreServiceMethods.ByName("GetRewards")), connect.WithHandlerOptions(opts...), ) + coreServiceGetRewardPoolHandler := connect.NewUnaryHandler( + CoreServiceGetRewardPoolProcedure, + svc.GetRewardPool, + connect.WithSchema(coreServiceMethods.ByName("GetRewardPool")), + connect.WithHandlerOptions(opts...), + ) coreServiceGetRewardAttestationHandler := connect.NewUnaryHandler( CoreServiceGetRewardAttestationProcedure, svc.GetRewardAttestation, @@ -672,6 +695,8 @@ func NewCoreServiceHandler(svc CoreServiceHandler, opts ...connect.HandlerOption coreServiceGetRewardHandler.ServeHTTP(w, r) case CoreServiceGetRewardsProcedure: coreServiceGetRewardsHandler.ServeHTTP(w, r) + case CoreServiceGetRewardPoolProcedure: + coreServiceGetRewardPoolHandler.ServeHTTP(w, r) case CoreServiceGetRewardAttestationProcedure: coreServiceGetRewardAttestationHandler.ServeHTTP(w, r) case CoreServiceGetRewardSenderAttestationProcedure: @@ -769,6 +794,10 @@ func (UnimplementedCoreServiceHandler) GetRewards(context.Context, *connect.Requ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.CoreService.GetRewards is not implemented")) } +func (UnimplementedCoreServiceHandler) GetRewardPool(context.Context, *connect.Request[v1.GetRewardPoolRequest]) (*connect.Response[v1.GetRewardPoolResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.CoreService.GetRewardPool is not implemented")) +} + func (UnimplementedCoreServiceHandler) GetRewardAttestation(context.Context, *connect.Request[v1.GetRewardAttestationRequest]) (*connect.Response[v1.GetRewardAttestationResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.CoreService.GetRewardAttestation is not implemented")) } diff --git a/pkg/common/legacy_reward_signing.go b/pkg/common/legacy_reward_signing.go new file mode 100644 index 00000000..fd8b6d96 --- /dev/null +++ b/pkg/common/legacy_reward_signing.go @@ -0,0 +1,69 @@ +package common + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "sort" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" +) + +// Legacy reward signing scheme used by the pre-pool-rollout network. Kept +// here so the new binary can recover signers from historical reward +// transactions during block-sync-from-genesis. New code paths must not use +// this scheme — sign / verify against the body+signature envelope instead. +// +// The scheme is a pipe-delimited canonical string of the action's fields +// (with claim authorities sorted and JSON-encoded), sha256-hashed, and +// signed with EthSign / recovered with EthRecover. It is NOT +// proto-deterministic and depends on the field set of the legacy proto +// types staying frozen. + +func LegacyDeterministicCreateRewardData(cr *corev1.LegacyCreateReward) string { + authorities := make([]string, len(cr.ClaimAuthorities)) + for i, auth := range cr.ClaimAuthorities { + authorities[i] = fmt.Sprintf("%s:%s", auth.Address, auth.Name) + } + sort.Strings(authorities) + authoritiesJson, _ := json.Marshal(authorities) + data := fmt.Sprintf("%s|%s|%d|%s|%d", + cr.RewardId, + cr.Name, + cr.Amount, + string(authoritiesJson), + cr.DeadlineBlockHeight) + hash := sha256.Sum256([]byte(data)) + return hex.EncodeToString(hash[:]) +} + +func LegacyDeterministicDeleteRewardData(dr *corev1.LegacyDeleteReward) string { + data := fmt.Sprintf("%s|%d", dr.Address, dr.DeadlineBlockHeight) + hash := sha256.Sum256([]byte(data)) + return hex.EncodeToString(hash[:]) +} + +// LegacyRecoverCreateReward returns the eth address that signed a legacy +// CreateReward transaction. +func LegacyRecoverCreateReward(cr *corev1.LegacyCreateReward) (string, error) { + signatureData := LegacyDeterministicCreateRewardData(cr) + dataBytes, err := hex.DecodeString(signatureData) + if err != nil { + return "", fmt.Errorf("invalid hex data: %w", err) + } + _, address, err := EthRecover(cr.Signature, dataBytes) + return address, err +} + +// LegacyRecoverDeleteReward returns the eth address that signed a legacy +// DeleteReward transaction. +func LegacyRecoverDeleteReward(dr *corev1.LegacyDeleteReward) (string, error) { + signatureData := LegacyDeterministicDeleteRewardData(dr) + dataBytes, err := hex.DecodeString(signatureData) + if err != nil { + return "", fmt.Errorf("invalid hex data: %w", err) + } + _, address, err := EthRecover(dr.Signature, dataBytes) + return address, err +} diff --git a/pkg/common/proto.go b/pkg/common/proto.go index e72d397c..235d7a81 100644 --- a/pkg/common/proto.go +++ b/pkg/common/proto.go @@ -19,29 +19,51 @@ func ToTxHashFromBytes(txBytes []byte) TxHash { return bytes.HexBytes(hash).String() } -// ProtoTxSign signs a protobuf message for transaction purposes -// WARNING: This should only be used when sending transactions and not replaying them -// as it's not safe from protobuf evolution. The message structure could change -// between versions, making signatures invalid. -func ProtoSign(pkey *ecdsa.PrivateKey, msg proto.Message) (string, error) { - msgBytes, err := proto.Marshal(msg) +// ProtoSign deterministically marshals body and signs the bytes with privKey. +// Callers pass the body (RewardBody, RewardPoolBody, etc.) — the body never +// contains its own signature, so there's no chicken-and-egg. The signature +// goes alongside the body in its envelope (RewardMessage, RewardPoolMessage) +// at transport time. +// +// Forward-compat: proto3 omits default-valued fields, so additive-only schema +// changes (new optional fields) don't shift the bytes for old signers that +// leave them unset. Existing signatures continue to verify as long as no +// caller removes / renames / changes-type-of an existing field. +// +// Cross-action replay protection: two action types share an envelope via a +// `oneof`, whose field tag is part of the marshaled bytes. Two actions with +// otherwise-identical inner shapes produce different signed bytes. +func ProtoSign(privKey *ecdsa.PrivateKey, body proto.Message) (string, error) { + b, err := signableBytes(body) if err != nil { - return "", fmt.Errorf("failed to marshal protobuf message: %w", err) + return "", fmt.Errorf("marshal for signing: %w", err) } - - return EthSign(pkey, msgBytes) + return EthSign(privKey, b) } -// ProtoTxRecover recovers the signer address from a protobuf message and signature -// WARNING: This should only be used when verifying transactions and not replaying them -// as it's not safe from protobuf evolution. The message structure could change -// between versions, making signatures invalid. -func ProtoRecover(msg proto.Message, signature string) (string, error) { - msgBytes, err := proto.Marshal(msg) +// ProtoRecover re-marshals body and recovers the eth address that produced +// signature. Returns the recovered address; the caller is responsible for +// any membership / authorization check. +func ProtoRecover(body proto.Message, signature string) (string, error) { + b, err := signableBytes(body) if err != nil { - return "", fmt.Errorf("failed to marshal protobuf message: %w", err) + return "", fmt.Errorf("marshal for recovery: %w", err) } - - _, address, err := EthRecover(signature, msgBytes) + _, address, err := EthRecover(signature, b) return address, err } + +// ProtoSignableBytes returns the deterministic-protobuf marshaling of body — +// the exact byte sequence ProtoSign / ProtoRecover hash and recover from. +// Exposed so callers that verify a second signature scheme (e.g., ed25519 +// alongside the secp256k1 envelope sig) can hash the same bytes without +// reaching into the package internals or having to duplicate the +// MarshalOptions. +func ProtoSignableBytes(body proto.Message) ([]byte, error) { + return signableBytes(body) +} + +func signableBytes(body proto.Message) ([]byte, error) { + opts := proto.MarshalOptions{Deterministic: true} + return opts.Marshal(body) +} diff --git a/pkg/common/proto_test.go b/pkg/common/proto_test.go new file mode 100644 index 00000000..63c64965 --- /dev/null +++ b/pkg/common/proto_test.go @@ -0,0 +1,166 @@ +package common + +import ( + "strings" + "testing" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/crypto" +) + +// TestProtoSignRecover_Roundtrip exercises every signed body type through +// ProtoSign + ProtoRecover. Whichever key signed, ProtoRecover returns its +// eth address. +func TestProtoSignRecover_Roundtrip(t *testing.T) { + priv, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("generate key: %v", err) + } + signer := crypto.PubkeyToAddress(priv.PublicKey).Hex() + + t.Run("RewardBody_Create", func(t *testing.T) { + body := &corev1.RewardBody{ + DeadlineBlockHeight: 999_999, + Action: &corev1.RewardBody_Create{Create: &corev1.CreateReward{ + RewardId: "r-1", + Name: "Reward 1", + Amount: 1000, + RewardsManagerPubkey: "DJj6F8oHLQM7Ec7FNh3sKWHJDZG7uH1zH7bPq3p1mUe2", + }}, + } + sig, err := ProtoSign(priv, body) + if err != nil { + t.Fatalf("sign: %v", err) + } + got, err := ProtoRecover(body, sig) + if err != nil { + t.Fatalf("recover: %v", err) + } + if !strings.EqualFold(got, signer) { + t.Fatalf("recovered %q, want %q", got, signer) + } + }) + + t.Run("RewardBody_Delete", func(t *testing.T) { + body := &corev1.RewardBody{ + DeadlineBlockHeight: 100, + Action: &corev1.RewardBody_Delete{Delete: &corev1.DeleteReward{Address: "0xreward"}}, + } + sig, err := ProtoSign(priv, body) + if err != nil { + t.Fatalf("sign: %v", err) + } + got, err := ProtoRecover(body, sig) + if err != nil { + t.Fatalf("recover: %v", err) + } + if !strings.EqualFold(got, signer) { + t.Fatalf("recovered %q, want %q", got, signer) + } + }) + + t.Run("RewardPoolBody_Create", func(t *testing.T) { + body := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 100, + Action: &corev1.RewardPoolBody_Create{Create: &corev1.CreateRewardPool{ + RewardsManagerPubkey: "DJj6F8oHLQM7Ec7FNh3sKWHJDZG7uH1zH7bPq3p1mUe2", + Authorities: []string{signer}, + }}, + } + sig, err := ProtoSign(priv, body) + if err != nil { + t.Fatalf("sign: %v", err) + } + got, err := ProtoRecover(body, sig) + if err != nil { + t.Fatalf("recover: %v", err) + } + if !strings.EqualFold(got, signer) { + t.Fatalf("recovered %q, want %q", got, signer) + } + }) + + t.Run("RewardPoolBody_SetAuthorities", func(t *testing.T) { + body := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 100, + Action: &corev1.RewardPoolBody_SetAuthorities{SetAuthorities: &corev1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: "DJj6F8oHLQM7Ec7FNh3sKWHJDZG7uH1zH7bPq3p1mUe2", + Authorities: []string{"0xnew"}, + }}, + } + sig, err := ProtoSign(priv, body) + if err != nil { + t.Fatalf("sign: %v", err) + } + got, err := ProtoRecover(body, sig) + if err != nil { + t.Fatalf("recover: %v", err) + } + if !strings.EqualFold(got, signer) { + t.Fatalf("recovered %q, want %q", got, signer) + } + }) +} + +// TestProtoSign_OneofTagDiscriminatesActions: two RewardPoolBodies — Create +// vs SetAuthorities — produce different signed bytes. The body's oneof field +// tag encodes which action variant is set, so even with otherwise-identical +// inner data the bytes (and thus signatures) diverge. +func TestProtoSign_OneofTagDiscriminatesActions(t *testing.T) { + priv, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("generate key: %v", err) + } + createBody := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 1, + Action: &corev1.RewardPoolBody_Create{Create: &corev1.CreateRewardPool{Authorities: []string{"0xa"}}}, + } + setBody := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 1, + Action: &corev1.RewardPoolBody_SetAuthorities{SetAuthorities: &corev1.SetRewardPoolAuthorities{RewardsManagerPubkey: "p", Authorities: []string{"0xa"}}}, + } + createSig, err := ProtoSign(priv, createBody) + if err != nil { + t.Fatalf("sign create: %v", err) + } + setSig, err := ProtoSign(priv, setBody) + if err != nil { + t.Fatalf("sign set: %v", err) + } + if createSig == setSig { + t.Fatalf("envelope-signed Create and SetAuthorities must differ; both are %q", createSig) + } +} + +// TestProtoSign_TamperingBreaksSignature: changing any field of the body +// (deadline, inner action data, etc.) after signing causes ProtoRecover to +// return a different (or no) signer. Replay with a tampered body fails. +func TestProtoSign_TamperingBreaksSignature(t *testing.T) { + priv, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("generate key: %v", err) + } + signer := crypto.PubkeyToAddress(priv.PublicKey).Hex() + + body := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 100, + Action: &corev1.RewardPoolBody_SetAuthorities{SetAuthorities: &corev1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: "DJj6F8oHLQM7Ec7FNh3sKWHJDZG7uH1zH7bPq3p1mUe2", + Authorities: []string{signer, "0xnew"}, + }}, + } + sig, err := ProtoSign(priv, body) + if err != nil { + t.Fatalf("sign: %v", err) + } + + // Tamper: extend the deadline. + tampered := &corev1.RewardPoolBody{ + DeadlineBlockHeight: 999_999, + Action: body.Action, + } + got, _ := ProtoRecover(tampered, sig) + if strings.EqualFold(got, signer) { + t.Fatalf("tampered deadline should not recover the original signer, got %q", got) + } +} diff --git a/pkg/common/reward_signing.go b/pkg/common/reward_signing.go deleted file mode 100644 index 7c0080f9..00000000 --- a/pkg/common/reward_signing.go +++ /dev/null @@ -1,69 +0,0 @@ -package common - -import ( - "crypto/ecdsa" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "sort" - - corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" -) - -// CreateDeterministicCreateRewardData creates deterministic hex data for signing CreateReward -func CreateDeterministicCreateRewardData(createReward *corev1.CreateReward) string { - // Sort claim authorities for deterministic ordering - authorities := make([]string, len(createReward.ClaimAuthorities)) - for i, auth := range createReward.ClaimAuthorities { - authorities[i] = fmt.Sprintf("%s:%s", auth.Address, auth.Name) - } - sort.Strings(authorities) - - authoritiesJson, _ := json.Marshal(authorities) - data := fmt.Sprintf("%s|%s|%d|%s|%d", - createReward.RewardId, - createReward.Name, - createReward.Amount, - string(authoritiesJson), - createReward.DeadlineBlockHeight) - - // Hash the data for consistent length - hash := sha256.Sum256([]byte(data)) - return hex.EncodeToString(hash[:]) -} - -// CreateDeterministicDeleteRewardData creates deterministic hex data for signing DeleteReward -func CreateDeterministicDeleteRewardData(deleteReward *corev1.DeleteReward) string { - data := fmt.Sprintf("%s|%d", deleteReward.Address, deleteReward.DeadlineBlockHeight) - - // Hash the data for consistent length - hash := sha256.Sum256([]byte(data)) - return hex.EncodeToString(hash[:]) -} - -// SignCreateReward signs a CreateReward message using deterministic data -func SignCreateReward(privateKey *ecdsa.PrivateKey, createReward *corev1.CreateReward) (string, error) { - signatureData := CreateDeterministicCreateRewardData(createReward) - - // Convert hex data to bytes for signing - dataBytes, err := hex.DecodeString(signatureData) - if err != nil { - return "", fmt.Errorf("invalid hex data: %w", err) - } - - return EthSign(privateKey, dataBytes) -} - -// SignDeleteReward signs a DeleteReward message using deterministic data -func SignDeleteReward(privateKey *ecdsa.PrivateKey, deleteReward *corev1.DeleteReward) (string, error) { - signatureData := CreateDeterministicDeleteRewardData(deleteReward) - - // Convert hex data to bytes for signing - dataBytes, err := hex.DecodeString(signatureData) - if err != nil { - return "", fmt.Errorf("invalid hex data: %w", err) - } - - return EthSign(privateKey, dataBytes) -} diff --git a/pkg/core/config/rewards.go b/pkg/core/config/rewards.go index bd522a3c..2b6d3ea2 100644 --- a/pkg/core/config/rewards.go +++ b/pkg/core/config/rewards.go @@ -2,6 +2,31 @@ package config import "github.com/OpenAudio/go-openaudio/pkg/rewards" +// Solana reward manager pubkeys for the AUDIO mint, per environment. +// Used as a denylist by validateCreateRewardPool: pools cannot be +// created for the AUDIO RM, so AUDIO sender attestations stay on the +// network-wide validator/AAO trust set rather than shifting to pool- +// controlled authorities. Empty values disable the denylist for that +// environment. +var ( + DevAudioRewardsManagerPubkey = "DJPzVothq58SmkpRb1ATn5ddN2Rpv1j2TcGvM3XsHf1c" + ProdAudioRewardsManagerPubkey = "71hWFVYokLaN1PNYzTAWi13EfJ7Xt9VbSWUKsXUT8mxE" +) + +// AudioRewardsManagerPubkey returns the configured AUDIO RM pubkey for +// the current runtime environment, or "" if none is configured. Callers +// MUST treat "" as "no denylist enforcement." +func AudioRewardsManagerPubkey() string { + switch GetRuntimeEnvironment() { + case "prod", "production", "mainnet": + return ProdAudioRewardsManagerPubkey + case "dev", "development", "devnet", "local", "sandbox": + return DevAudioRewardsManagerPubkey + default: + return "" + } +} + var ( DevClaimAuthorities = []rewards.ClaimAuthority{ { diff --git a/pkg/core/db/models.go b/pkg/core/db/models.go index 1fb14caa..9806753b 100644 --- a/pkg/core/db/models.go +++ b/pkg/core/db/models.go @@ -247,19 +247,26 @@ type CoreResource struct { } type CoreReward struct { - ID int64 - Address string - Index int64 - TxHash string - Sender string - RewardID string - Name string - Amount int64 - ClaimAuthorities []string - RawMessage []byte - BlockHeight int64 - CreatedAt pgtype.Timestamptz - UpdatedAt pgtype.Timestamptz + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + RawMessage []byte + BlockHeight int64 + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz + RewardsManagerPubkey pgtype.Text +} + +type CoreRewardPool struct { + RewardsManagerPubkey string + Authorities []string + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz } type CoreTransaction struct { @@ -305,6 +312,12 @@ type CoreValidator struct { Jailed bool } +type LaunchpadAuthorityRm struct { + Authority string + RewardsManagerPubkey string + CreatedAt pgtype.Timestamptz +} + type ManagementKey struct { ID int32 TrackID string diff --git a/pkg/core/db/reads.sql.go b/pkg/core/db/reads.sql.go index 41d54062..b1731fa3 100644 --- a/pkg/core/db/reads.sql.go +++ b/pkg/core/db/reads.sql.go @@ -12,20 +12,41 @@ import ( ) const getActiveRewards = `-- name: GetActiveRewards :many -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at -from core_rewards -order by address -` - -func (q *Queries) GetActiveRewards(ctx context.Context) ([]CoreReward, error) { +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +order by r.address +` + +type GetActiveRewardsRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +func (q *Queries) GetActiveRewards(ctx context.Context) ([]GetActiveRewardsRow, error) { rows, err := q.db.Query(ctx, getActiveRewards) if err != nil { return nil, err } defer rows.Close() - var items []CoreReward + var items []GetActiveRewardsRow for rows.Next() { - var i CoreReward + var i GetActiveRewardsRow if err := rows.Scan( &i.ID, &i.Address, @@ -38,6 +59,7 @@ func (q *Queries) GetActiveRewards(ctx context.Context) ([]CoreReward, error) { &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ); err != nil { @@ -215,23 +237,41 @@ func (q *Queries) GetAllRegisteredNodesSorted(ctx context.Context) ([]CoreValida } const getAllRewards = `-- name: GetAllRewards :many -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at from core_rewards -where address in ( - select distinct address - from core_rewards -) -order by block_height desc -` - -func (q *Queries) GetAllRewards(ctx context.Context) ([]CoreReward, error) { +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +order by r.block_height desc +` + +type GetAllRewardsRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +func (q *Queries) GetAllRewards(ctx context.Context) ([]GetAllRewardsRow, error) { rows, err := q.db.Query(ctx, getAllRewards) if err != nil { return nil, err } defer rows.Close() - var items []CoreReward + var items []GetAllRewardsRow for rows.Next() { - var i CoreReward + var i GetAllRewardsRow if err := rows.Scan( &i.ID, &i.Address, @@ -244,6 +284,7 @@ func (q *Queries) GetAllRewards(ctx context.Context) ([]CoreReward, error) { &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ); err != nil { @@ -1442,6 +1483,30 @@ func (q *Queries) GetLatestSlaRollup(ctx context.Context) (SlaRollup, error) { return i, err } +const getLaunchpadRMByAuthority = `-- name: GetLaunchpadRMByAuthority :one +select rewards_manager_pubkey +from launchpad_authority_rm +where authority = any($1::text[]) +order by rewards_manager_pubkey, authority +limit 1 +` + +// 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 the 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 the migration 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). +func (q *Queries) GetLaunchpadRMByAuthority(ctx context.Context, dollar_1 []string) (string, error) { + row := q.db.QueryRow(ctx, getLaunchpadRMByAuthority, dollar_1) + var rewards_manager_pubkey string + err := row.Scan(&rewards_manager_pubkey) + return rewards_manager_pubkey, err +} + const getMEAD = `-- name: GetMEAD :one select id, address, tx_hash, index, sender, resource_addresses, release_addresses, raw_message, raw_acknowledgment, block_height from core_mead where address = $1 order by block_height desc limit 1 ` @@ -2017,15 +2082,37 @@ func (q *Queries) GetRegisteredNodesByType(ctx context.Context, nodeType string) } const getReward = `-- name: GetReward :one -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at from core_rewards -where address = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.address = $1 +order by r.block_height desc limit 1 ` -func (q *Queries) GetReward(ctx context.Context, address string) (CoreReward, error) { +type GetRewardRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +func (q *Queries) GetReward(ctx context.Context, address string) (GetRewardRow, error) { row := q.db.QueryRow(ctx, getReward, address) - var i CoreReward + var i GetRewardRow err := row.Scan( &i.ID, &i.Address, @@ -2038,6 +2125,7 @@ func (q *Queries) GetReward(ctx context.Context, address string) (CoreReward, er &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ) @@ -2045,15 +2133,37 @@ func (q *Queries) GetReward(ctx context.Context, address string) (CoreReward, er } const getRewardByID = `-- name: GetRewardByID :one -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at from core_rewards -where reward_id = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.reward_id = $1 +order by r.block_height desc limit 1 ` -func (q *Queries) GetRewardByID(ctx context.Context, rewardID string) (CoreReward, error) { +type GetRewardByIDRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +func (q *Queries) GetRewardByID(ctx context.Context, rewardID string) (GetRewardByIDRow, error) { row := q.db.QueryRow(ctx, getRewardByID, rewardID) - var i CoreReward + var i GetRewardByIDRow err := row.Scan( &i.ID, &i.Address, @@ -2066,6 +2176,7 @@ func (q *Queries) GetRewardByID(ctx context.Context, rewardID string) (CoreRewar &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ) @@ -2073,15 +2184,37 @@ func (q *Queries) GetRewardByID(ctx context.Context, rewardID string) (CoreRewar } const getRewardByTxHash = `-- name: GetRewardByTxHash :one -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at from core_rewards -where tx_hash = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.tx_hash = $1 +order by r.block_height desc limit 1 ` -func (q *Queries) GetRewardByTxHash(ctx context.Context, txHash string) (CoreReward, error) { +type GetRewardByTxHashRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +func (q *Queries) GetRewardByTxHash(ctx context.Context, txHash string) (GetRewardByTxHashRow, error) { row := q.db.QueryRow(ctx, getRewardByTxHash, txHash) - var i CoreReward + var i GetRewardByTxHashRow err := row.Scan( &i.ID, &i.Address, @@ -2094,28 +2227,103 @@ func (q *Queries) GetRewardByTxHash(ctx context.Context, txHash string) (CoreRew &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ) return i, err } -const getRewardsByClaimAuthority = `-- name: GetRewardsByClaimAuthority :many -select id, address, index, tx_hash, sender, reward_id, name, amount, claim_authorities, raw_message, block_height, created_at, updated_at -from core_rewards -where $1::text = any(claim_authorities) -order by address +const getRewardPool = `-- name: GetRewardPool :one +select rewards_manager_pubkey, authorities, created_at, updated_at +from core_reward_pools +where rewards_manager_pubkey = $1 +` + +func (q *Queries) GetRewardPool(ctx context.Context, rewardsManagerPubkey string) (CoreRewardPool, error) { + row := q.db.QueryRow(ctx, getRewardPool, rewardsManagerPubkey) + var i CoreRewardPool + err := row.Scan( + &i.RewardsManagerPubkey, + &i.Authorities, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getRewardPoolsByAuthority = `-- name: GetRewardPoolsByAuthority :many +select rewards_manager_pubkey, authorities, created_at, updated_at +from core_reward_pools +where authorities @> array[$1::text] +order by rewards_manager_pubkey ` -func (q *Queries) GetRewardsByClaimAuthority(ctx context.Context, dollar_1 string) ([]CoreReward, error) { +// Uses array containment (@>) so the gin index on authorities is used. +func (q *Queries) GetRewardPoolsByAuthority(ctx context.Context, dollar_1 string) ([]CoreRewardPool, error) { + rows, err := q.db.Query(ctx, getRewardPoolsByAuthority, dollar_1) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CoreRewardPool + for rows.Next() { + var i CoreRewardPool + if err := rows.Scan( + &i.RewardsManagerPubkey, + &i.Authorities, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getRewardsByClaimAuthority = `-- name: GetRewardsByClaimAuthority :many +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where p.authorities @> array[$1::text] +order by r.address +` + +type GetRewardsByClaimAuthorityRow struct { + ID int64 + Address string + Index int64 + TxHash string + Sender string + RewardID string + Name string + Amount int64 + ClaimAuthorities []string + RawMessage []byte + BlockHeight int64 + RewardsManagerPubkey pgtype.Text + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz +} + +// Uses array containment (@>) so the gin index on core_reward_pools.authorities +// can be used. = ANY(...) cannot leverage the gin opclass. +func (q *Queries) GetRewardsByClaimAuthority(ctx context.Context, dollar_1 string) ([]GetRewardsByClaimAuthorityRow, error) { rows, err := q.db.Query(ctx, getRewardsByClaimAuthority, dollar_1) if err != nil { return nil, err } defer rows.Close() - var items []CoreReward + var items []GetRewardsByClaimAuthorityRow for rows.Next() { - var i CoreReward + var i GetRewardsByClaimAuthorityRow if err := rows.Scan( &i.ID, &i.Address, @@ -2128,6 +2336,7 @@ func (q *Queries) GetRewardsByClaimAuthority(ctx context.Context, dollar_1 strin &i.ClaimAuthorities, &i.RawMessage, &i.BlockHeight, + &i.RewardsManagerPubkey, &i.CreatedAt, &i.UpdatedAt, ); err != nil { diff --git a/pkg/core/db/sql/migrations/00034_reward_pools.sql b/pkg/core/db/sql/migrations/00034_reward_pools.sql new file mode 100644 index 00000000..9fe949cf --- /dev/null +++ b/pkg/core/db/sql/migrations/00034_reward_pools.sql @@ -0,0 +1,243 @@ +-- +migrate Up + +-- Reward pools group the eth addresses authorized to attest for rewards +-- under a specific Solana reward manager. The pool is identified BY the +-- reward manager pubkey (32-byte base58) — there is no separate "pool +-- address" concept. The CreateRewardPool / SetRewardPoolAuthorities +-- cometbft txs mutate this table; the validator's sender-attestation +-- gate consults it to decide whether to sign add/delete attestations +-- for a given (RM, eth address) pair. +create table if not exists core_reward_pools ( + rewards_manager_pubkey text primary key, + authorities text[] not null default '{}', + created_at timestamp with time zone default now(), + updated_at timestamp with time zone default now() +); + +create index if not exists idx_core_reward_pools_authorities on core_reward_pools using gin (authorities); + +-- launchpad_authority_rm maps a launchpad-derived per-mint claim authority +-- (lowercased eth hex address) to the Solana reward manager state account +-- that mint's rewards live under. The values are produced by running +-- DeriveEthAddressForMint(domain="claimAuthority", secret, mint) for each +-- known launchpad mint and recording its RM. This table serves two +-- purposes: +-- +-- 1. The backfill block below uses it to find each existing reward row's +-- RM (by looking for any one of its claim_authorities that matches a +-- known launchpad authority). +-- 2. The wire-compat layer (finalizeLegacyCreateReward) queries it +-- at block-sync replay time to produce the same RM-bound state +-- the migration produces, so historical replay and the migration +-- agree on the resulting apphash. +-- +-- The table must remain populated and queryable as long as any legacy- +-- format CreateReward txs may still be replayed during block sync. Future +-- launchpad mints can be added via additional migrations as they're +-- registered. +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() +); + +insert into launchpad_authority_rm (authority, rewards_manager_pubkey) values + ('0x0c5b590a071a377c49a154639fd4343d147bcafe', 'urGNtpg4ppvLKH5Yvkmi1dixbTsokJdAdbzGs9iSSPy'), + ('0x103cf68f8d352ba3dbee8b56236befaa1e1429aa', 'BWC8rj4am5izbJXCpwyEfTxAo2StWGiwjBfSzAzKt4Gi'), + ('0x111d7c8cb8c46f743a7796a7f9c0f2fb08076f6f', '7hXopQnXfv8tRf8aVXvGauAHyUPcNKAT4gp49ufhxk36'), + ('0x1165e5f035ce5eeaed37c6ad190b58903bac6175', 'Di77DoUUbe8QDq6XU3jptxMLnT8S5kj8JZ3qGYRTHNKh'), + ('0x14035c457a724b83da6c88b7dca0d283d2789808', 'dWwazU2QUih1pDnuGWXfJnVG6uLfvogqnFknvrxju92'), + ('0x14637fad403350a1fdbd6d21cc7658ae46d7bbb0', '5z5TV5WhbUyhJDkhmeKsJLNgreVcNq3kNh3Kwd2mGHEE'), + ('0x16ac1f19e207794ff1462b2ffdad742b3b3c0ce7', 'Bvr2HYVpKThU4nCFhpDy7Y6XLBMZUuQHK1KRbAGnCiQt'), + ('0x1a17cf24fa74847c4c2e4e14d9fa5d55347ea829', 'FGrk4MQGFyjsgnCBCoCpRgMubo6SqSwvHYiEHuDnXrcE'), + ('0x1be07e9b5af6cad186f8e3771d97e11b3af27dbe', 'FDeFBV6XntX2LkkYFmBSyqhwf8w7MzvND3kcJWYD4gVa'), + ('0x1edf70aaf55952a8d9d75a550e0f7eef6593c0b9', 'GBDpJArbu7Mes5FnXYgMNAaUdayCAGRzpJrThaKr9dsS'), + ('0x212fbc84a052d73b9dbceae3c8de29597db64d0f', 'E7fZSM55CzM7gD9LcnN5jEakKJLcHUwyNQZc9RL2RgTT'), + ('0x21adafbd269728c08f2ee6d3a3a43455b7e09ad7', '7Lra25qHo7yJWTQjwAGcyK3PvHXLfKpqNZkpGX76dzkK'), + ('0x27ca8265e6d9f0a95657fa7d36964b10d826fa6d', 'BtU6seJeyf5b68mSA2iFH1wdipxNHWSuU9JGKhAYVtry'), + ('0x29063c045b421d064993bde6e98646b47998c4f7', 'CDZ6nAr8md5xz2Wvb3LdnLya6yjxpqcLTMZrb8zYMdcM'), + ('0x36d1b69132f70558dc4838d5ae87cca21c562f1f', '2QJhBYiEznpjgSSsezcccpfkYdngPik4fvQzxHMucm9L'), + ('0x3b3c30eb5dd9bb6f9b92a14448474242513299b2', 'GXX15br7UgnkhUskBtfTn26SsWbtjijofxGH5Be1LKqP'), + ('0x3e472953a85573bf25c657772a5f05624de5d9f1', 'FhHiVewd3dM63nJ6h3cZFdvMcyFdVJURhHUfYvvXDpQm'), + ('0x48f31a62c79ead300514cb890e68da88acc018e8', '3zaWKMBCR2mDoKCGjtyB4Y24xHQa1XdT2tGXuRThKg8U'), + ('0x4b797d0822d595de3e3ac3f2747bd5fdc2dfe96d', 'AvGcJBQpiWTvFFXW7GHYZXHpuNxjvGpZ8ezpjQXf2KN9'), + ('0x4e870de81a26d7e84dee34e8678b5958fe8730d1', '8CtDHsjPUpnGyNSAQpwGqPm46Np6jJWxRpzXH2277RX3'), + ('0x4fff490d410d5f412392bbad9e5b2b18742eea6c', '8DY6uaCyzmSXNneycc45tS1TxUTPjuYiFQYtsjWsASSN'), + ('0x503d34f7615b2a12651a54286f0d27d732647993', 'pRS2Gkt9YNoU1G9HAXeRruKGN5qRUQsg5pVxbtAuPmV'), + ('0x50dec0d97ee06a32c81651b8430ff424d6c58dc6', '4Aa5UrKqeUPrUVHRjiGAF6o1V3u17v2rQmERNEXLNkZ9'), + ('0x550d7cb8e314828c29bd48ce8e732e5799efe08d', 'GGrpfPa8gXPQ3FEusXhsPQBnWeNqb7Z1cfSD6gY3zunQ'), + ('0x5ad1a050b693f59eb64e4ac6d34bf30c3880cd24', 'BNJMsdNJN4NRJcisFtPbWF799eKa19YafLs6oDZAYDGm'), + ('0x5c78fe2d276450237d569bc4afa763bf9ccaf74f', 'GCrhftvMubAEQrnCcTUxLHxsCbnGr7wVdLtwjj1SkWQN'), + ('0x5f7f7dacbc1f64e28d465cb2644592963771d618', 'CBaP4ncJYxv2wmjT8bxtpuJ41wJU8SkbMKuaP4PP9S8C'), + ('0x66a9d306336e227a47a3486669bba023b141192d', '65EKqnYE8Rom3DnLGP7XbXjyNY8xXMfCnpjMZW14tsvV'), + ('0x6702d3bff4b3736cf72f9f64db4169a5853dbc9c', '83ENNoux1oKhMndc2Uw6BqwWTPHPavHWQhG292bZun3N'), + ('0x6b13053a2b4c7fc71d60d58dd55a97b34b245a80', '2WdvdUbEEY1XRYVQmrHFipTWg8Jy42nE4e7xFK3Q6gjo'), + ('0x6c6da83a8237ee4dc4ecfea880c17c4ed6d7a2c8', 'F3xcZ7jQFWBgp2kMjp9fHSkCehRWzuVND6n1mCu1oyjH'), + ('0x832d886079387f3ab825e978bfc682f6c158a3e8', '8FkZU5VooBFXqb8GA36kgTyHZ5J1ABqi13bBKzkExvBz'), + ('0x85f54509c6075ed7efe6889a85b713cb68ce56cf', '6UuCBYGutteDTYAhd1W5PpvvSAg4LEWiJYQnWVU79bER'), + ('0x868d17a61edafd0b9d96032cd44570a95ff6a04f', '86jE4ubrbFGwdwTv6iN2FZpQm2EiHXru4teowrYpu8Tn'), + ('0x86a60f5b2b3f29a42c80fec03818bd329655187f', '4kmzFHSSrUxeTkfRrYmKv96o7guqi7LPBaZrzqvKQYL5'), + ('0x8719de0df16a3e0889d8509b351dafabee4ea294', 'GaDBQ27F7EmNHumchE6Pjr43frckBuxsUUJjN45uAajQ'), + ('0x8ad3ca46321b18e06b35d4c8e76d18d976de7727', 'GQxHiQFfeby9wfyUw5Bw1Q1xPB7ZqwgKjB36PtHhKstV'), + ('0x8f4458d8f0ae1b3f83e84be178fd1c4f3fc36bd4', 'CAQm1eMbUn3j5qhFWT4FmnDzwuNddy1supzR5A85VVfW'), + ('0x90aaffd8c2bbb0b68e99b90319c3c7bb43d33eb2', 'mY3V2Y2Trjsaa3qHrrL7s3aNWEG6getJUdpXvf71uqe'), + ('0x9293d36202216e47afb9d7762f05ad52c59403e4', 'AdDEp4rJe7RAFRg14uaFMUmPfLHJ6oJeSe49aXSwPVgZ'), + ('0x9d7ba5f04c7dc2993ff2ccdf19e29ad741036e90', '78XW1jt6T5mXTBocoDuTqB4RAM3mAKnYGSYWDeXY135s'), + ('0x9f3ebdf813c04ef8096326dd134270b376525e1a', 'fWcM3QvikFoZGKQ9eJ8yZ4Q7SVcHydnGMoR7fdrtza9'), + ('0xa1ef8ef283501d456b76757b32ea345cdcb4224f', '8u3ejuKz7uywPqdz5fS8cB4ppXvAZ3WiLeVKdiFWoK2r'), + ('0xa29dff72f7aecb95d1414f7206eb69820ec86cc7', '2hcSdCmtCfgNhjBELh6a9GJ2WSbbU8LMMonqFLZTAp67'), + ('0xa85281a327c0848a619659391eb88dd3702fcf41', '781STZvPpSTaDNcB35tbAWk2fwbTpqaRNRpSQpun7C3X'), + ('0xaa49c2afad744ac3595baed0d1f9c7876720c9df', 'EUqRiCAgj7QnP176ThaGfrGz3r2VxxNyXuAPU5SPhMJS'), + ('0xad6b9555369a438fb4698c46dfad245ef97b270e', '8jrWskYgwYPEZHSUCKnQmdq4hGKgAt9gcZeXtQCsRnJs'), + ('0xb3f3f879c7c0cb0a4af1fcddb6557fc2b3963bc9', '3Z4GXG84TjmvnV8h16qraFmWeEBv2eQd9quc39UhwiAe'), + ('0xb61525df350dbfc6a2ac82412f6ece0cc4dbd7ff', 'CFawCKFwxqLejDdoyUMjyBQokX2n37E37oS8HAUogmAM'), + ('0xbbecbfecf49bb3159c67fa5d254c40307c6fce2b', '86Wqf14Hj3DQMwgpuqQtAApJT3Wyf4wR7wWmdYzfkLWZ'), + ('0xbc731ab6905ef383b519702dffbe8bb3f9ccf547', 'EgTvyvn9QDsAyXVamM8pzV2YHoVRuWBVmRFD56BaGgkS'), + ('0xbd00af91f78a651bdb74892f7c7207ffed7ef3dd', 'A3ZhYjqJfBZtJxUGXXruYHSWR5Zf7NaBgteTwEhrKwow'), + ('0xc402938db359d2644df952697dc3925c62fef99e', 'B4ijMf1byKLmRUV9RZaDCZqs4TTPGEfbEYD67B5M22Z2'), + ('0xc82c810ae45ba45d5ccdc02273f48e453d51e2f9', '6YAc7rLW8uom3DRnj6wkwvq6qpW9PkAfxBxXpU2wyyph'), + ('0xc8acdb3049903f6758c434d21b77af2b8af48d9e', 'ELs98dgSh3Kd6ViMM9LFT1rfEbPvm4e6xpQzjK2wVjH6'), + ('0xce61db54629ef47d0dfb2923660c787ac92fcec1', 'P76iaELTfBg1hKo6hr22scupEMhDsAcayg5AMGpQiCM'), + ('0xcece39acb112bfbc59fe41606d4475435f1b3676', '4E7B4BGd2bfjutdjkWsMcjgubGkxgf5niNNuKQKhbfpQ'), + ('0xda1a09589098954b05648419cc4bbea20c6e39ea', '46w1RsTcygL5C6ijfaAztUstPqHuzZXPEB2KCr52cYHf'), + ('0xdaef8d116025a7053577c95cf793827cbf6d7767', '7ij5BsYE2aL3QG2Mt1ifB3mNZx9ELXBEZbCMX6nLM977'), + ('0xe325971fe787077e8b6d42e9f353eea06a801680', '31Dq71KBhKEs2nEwytX8RwEC1wBS2pUaQdPVk3zTHsWP'), + ('0xe9448fb249fb675f6d51483edef063bf64487a3f', 'GqHmpeQkqNW7h8mPUQPsXqy3E5CVy1siMbeHuU9uhNVi'), + ('0xee878a78a703a67c73d1e794e61bb36fc715eef2', '6TW5NzdMF7ps4zaDo6ogCV1iUHde4VdA2ZyNtJKLwSLp'), + ('0xf0ed91252a509ff2d24f0dd6e22c0e985f636334', 'BrY2VSnagcjsr6DND1FpcfpYiJ8PrBdheuPRggKVfZUN'), + ('0xf174de7442c805c3900ce4140a81f329c5a7f5bc', '3G9yANcxNcFobZgZbP3TyQuF4zqC3hqeqEi1LDcJjJgo'), + ('0xf3baf2379bb060fa63f620576fbb977ef5fa4e51', '8dZLVViKogvR8nPFVLcK64r32WG5GKZojn2BTvJ2LHHL'), + ('0xfa29072ea2933a8ece9842d19480184ef9583ba8', '8ThSmVGV6NW36JDuVgNCgYuh6b9QycaQCmRT18boS4aR'), + ('0xfe3d5e7b8bab7599d63f43dcd673f7c27424296e', 'G1YQE4itykWEWSheCzKMKVma7D7AYxrQ7UY92DvZLUeV'), + ('0xffbb95b5631c39e860016d23918592489031248c', '454bPX92ayZf6XmuGRjFGKGAr43hYX2T8U3avcd6KNGi') +on conflict (authority) do nothing; + +-- Add rewards_manager_pubkey FK to core_rewards. Nullable: rows whose +-- claim_authorities don't include any known launchpad-derived authority +-- (e.g., test fixtures, abandoned rewards) stay NULL after the backfill +-- and have no pool. The validator's sender-attestation gate refuses to +-- operate on NULL-RM rewards (no pool, no authority data). +alter table core_rewards add column if not exists rewards_manager_pubkey text; +create index if not exists idx_core_rewards_rewards_manager_pubkey on core_rewards (rewards_manager_pubkey); + +-- Backfill step 1: insert one core_reward_pools row per (RM, canonical +-- authorities) pair seen in core_rewards. The pool's RM is determined by +-- finding any one of the row's claim_authorities that matches a launchpad +-- authority — so each unique authority set becomes a real RM-bound pool. +-- The pool's authorities array is the canonical (trim, lower, dedup, +-- sort) form of the row's full claim_authorities, preserving any +-- additional entries as the pool's *initial* authorities; rotation via +-- SetRewardPoolAuthorities is the surface that drains them out. +-- Per-RM authority union: for each RM, take the UNION of authorities +-- across every reward whose claim_authorities contain ANY of that RM's +-- launchpad-derived keys. This guards against the case where two +-- rewards under the same mint were created with slightly different +-- authority sets (e.g., one had an additional debug key) — a naive +-- "ON CONFLICT DO NOTHING" insert would keep an arbitrary +-- first-inserted set and silently drop authorities present on other +-- rewards. The union preserves all of them as the pool's initial set; +-- SetRewardPoolAuthorities is the rotation surface that drains them. +insert into core_reward_pools (rewards_manager_pubkey, authorities) +select + rm.rewards_manager_pubkey, + array( + select distinct lower(trim(a)) + from core_rewards r, + unnest(r.claim_authorities) as a + where r.claim_authorities is not null + and array_length(r.claim_authorities, 1) > 0 + and a is not null + and trim(a) <> '' + and exists ( + select 1 + from unnest(r.claim_authorities) as r_auth + where lower(trim(r_auth)) = rm.authority + ) + order by 1 + ) as authorities +from launchpad_authority_rm rm +where exists ( + select 1 + from core_rewards r, + unnest(r.claim_authorities) as r_auth + where r.claim_authorities is not null + and array_length(r.claim_authorities, 1) > 0 + and lower(trim(r_auth)) = rm.authority +) +on conflict (rewards_manager_pubkey) do update set + authorities = excluded.authorities, + updated_at = now(); + +-- Backfill step 2: point each core_rewards row at its pool. We pick the RM +-- from any launchpad-derived authority found among the row's +-- claim_authorities. In practice each row should contain exactly one +-- launchpad authority (the per-mint key for the coin the reward was issued +-- under), so the lateral lookup yields a unique RM; rows with no matching +-- authority are left with NULL rewards_manager_pubkey. +update core_rewards r +set rewards_manager_pubkey = mapped.rewards_manager_pubkey +from ( + select + r2.address as reward_address, + rm.rewards_manager_pubkey + from core_rewards r2 + cross join lateral ( + select rm.rewards_manager_pubkey + from launchpad_authority_rm rm + where rm.authority = any ( + select lower(trim(a)) from unnest(r2.claim_authorities) a + where a is not null and trim(a) <> '' + ) + order by rm.rewards_manager_pubkey, rm.authority + limit 1 + ) rm + where r2.claim_authorities is not null + and array_length(r2.claim_authorities, 1) > 0 +) mapped +where r.address = mapped.reward_address; + +-- Rows whose claim_authorities don't include any launchpad-mapped per-mint +-- key are intentionally left with NULL rewards_manager_pubkey and have no +-- pool. The launchpad_authority_rm seed above is the complete set of RM +-- init events; rows that don't match are stale fixture data, never live +-- production rewards. Such rewards are unclaimable post-migration; their +-- claim authority recovers by submitting CreateRewardPool + a fresh +-- CreateReward via the cometbft tx surface. + +alter table core_rewards + add constraint fk_core_rewards_rewards_manager_pubkey + foreign key (rewards_manager_pubkey) references core_reward_pools(rewards_manager_pubkey) + on delete cascade; + +-- The row's rewards_manager_pubkey now carries the authorization signal; +-- the inline claim_authorities column and its gin index are redundant. +drop index if exists idx_core_rewards_claim_authorities; +alter table core_rewards drop column if exists claim_authorities; + +-- +migrate Down + +-- Restore the column (backfilled from the pool's authorities) before tearing +-- down the FK so existing data is preserved on a downgrade. +-- +-- KNOWN LIMITATION: rewards whose claim_authorities did not include any +-- launchpad-derived authority were left with NULL rewards_manager_pubkey +-- by the Up migration (no pool was created for them). Those rows have no +-- pool to restore from, so the JOIN below leaves their claim_authorities +-- as the column default ('{}'). The original inline claim_authorities are +-- not recoverable on a downgrade. This is acceptable because the Down +-- migration is a best-effort rollback path — production rollback would +-- happen at the chain level, not via SQL Down. +alter table core_rewards add column if not exists claim_authorities text[] default '{}'; +update core_rewards r +set claim_authorities = coalesce(p.authorities, '{}'::text[]) +from core_reward_pools p +where p.rewards_manager_pubkey = r.rewards_manager_pubkey; +create index if not exists idx_core_rewards_claim_authorities on core_rewards using gin (claim_authorities); + +alter table core_rewards drop constraint if exists fk_core_rewards_rewards_manager_pubkey; +drop index if exists idx_core_rewards_rewards_manager_pubkey; +alter table core_rewards drop column if exists rewards_manager_pubkey; + +drop index if exists idx_core_reward_pools_authorities; +drop table if exists core_reward_pools; +drop table if exists launchpad_authority_rm; diff --git a/pkg/core/db/sql/reads.sql b/pkg/core/db/sql/reads.sql index edeeb88d..f7d2ae3e 100644 --- a/pkg/core/db/sql/reads.sql +++ b/pkg/core/db/sql/reads.sql @@ -586,41 +586,95 @@ where b.height = $1 order by b.height, t.index asc; -- name: GetReward :one -select * from core_rewards -where address = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.address = $1 +order by r.block_height desc limit 1; -- name: GetRewardByID :one -select * from core_rewards -where reward_id = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.reward_id = $1 +order by r.block_height desc limit 1; -- name: GetRewardByTxHash :one -select * from core_rewards -where tx_hash = $1 -order by block_height desc +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where r.tx_hash = $1 +order by r.block_height desc limit 1; -- name: GetAllRewards :many -select * from core_rewards -where address in ( - select distinct address - from core_rewards -) -order by block_height desc; +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +order by r.block_height desc; -- name: GetActiveRewards :many -select * -from core_rewards -order by address; +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +left join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +order by r.address; -- name: GetRewardsByClaimAuthority :many -select * -from core_rewards -where $1::text = any(claim_authorities) -order by address; +-- Uses array containment (@>) so the gin index on core_reward_pools.authorities +-- can be used. = ANY(...) cannot leverage the gin opclass. +select + r.id, r.address, r.index, r.tx_hash, r.sender, r.reward_id, r.name, r.amount, + coalesce(p.authorities, '{}'::text[])::text[] as claim_authorities, + r.raw_message, r.block_height, r.rewards_manager_pubkey, r.created_at, r.updated_at +from core_rewards r +join core_reward_pools p on p.rewards_manager_pubkey = r.rewards_manager_pubkey +where p.authorities @> array[$1::text] +order by r.address; + +-- name: GetRewardPool :one +select rewards_manager_pubkey, authorities, created_at, updated_at +from core_reward_pools +where rewards_manager_pubkey = $1; + +-- name: GetRewardPoolsByAuthority :many +-- Uses array containment (@>) so the gin index on authorities is used. +select rewards_manager_pubkey, authorities, created_at, updated_at +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 the 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 the migration 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; diff --git a/pkg/core/db/sql/writes.sql b/pkg/core/db/sql/writes.sql index 26a01f05..bd5b70dc 100644 --- a/pkg/core/db/sql/writes.sql +++ b/pkg/core/db/sql/writes.sql @@ -337,7 +337,7 @@ insert into core_rewards ( reward_id, name, amount, - claim_authorities, + rewards_manager_pubkey, raw_message, block_height ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10); @@ -346,7 +346,7 @@ insert into core_rewards ( update core_rewards set name = $2, amount = $3, - claim_authorities = $4, + rewards_manager_pubkey = $4, raw_message = $5, block_height = $6, updated_at = now() @@ -356,6 +356,52 @@ where address = $1; delete from core_rewards where address = $1; +-- name: UpsertLegacyReplayRewardPool :exec +-- Used only by finalizeLegacyCreateReward during block-sync replay of +-- legacy-shape CreateReward bytes. Materializes a real-RM pool from the +-- legacy reward's inline claim_authorities. +-- +-- DO UPDATE unions the new authorities with the existing set rather than +-- overwriting. This matches the migration backfill, which UNIONs authorities +-- across every legacy reward referencing the RM. Without the union, a +-- from-genesis-syncing node would end up with +-- pool.authorities = last-replayed-reward.authorities, while an in-place- +-- upgraded node would have UNION(every reward) — different DB state ⇒ +-- different validation outcomes for downstream txs ⇒ apphash divergence. +insert into core_reward_pools ( + rewards_manager_pubkey, + authorities +) values ($1, $2) +on conflict (rewards_manager_pubkey) do update set + authorities = ( + select array( + select distinct lower(trim(a)) + from unnest(core_reward_pools.authorities || excluded.authorities) as a + where a is not null and trim(a) <> '' + order by 1 + ) + ), + 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; +-- uniqueness is validated at validate time and same-block collisions +-- surface here as a PK violation that fails the second tx in the block. +insert into core_reward_pools ( + rewards_manager_pubkey, + authorities +) values ($1, $2); + +-- name: UpdateRewardPoolAuthorities :exec +-- Replaces the authority set on an existing pool wholesale. Used by the +-- SetRewardPoolAuthorities finalizer; callers compose the new list themselves +-- (current minus the removed key, current plus the added key, etc.). +update core_reward_pools +set authorities = $2, + updated_at = now() +where rewards_manager_pubkey = $1; + -- name: InsertFileUpload :exec insert into core_uploads( uploader_address, diff --git a/pkg/core/db/writes.sql.go b/pkg/core/db/writes.sql.go index 84cfdb01..09418160 100644 --- a/pkg/core/db/writes.sql.go +++ b/pkg/core/db/writes.sql.go @@ -407,23 +407,23 @@ insert into core_rewards ( reward_id, name, amount, - claim_authorities, + rewards_manager_pubkey, raw_message, block_height ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ` type InsertCoreRewardParams struct { - Address string - TxHash string - Index int64 - Sender string - RewardID string - Name string - Amount int64 - ClaimAuthorities []string - RawMessage []byte - BlockHeight int64 + Address string + TxHash string + Index int64 + Sender string + RewardID string + Name string + Amount int64 + RewardsManagerPubkey pgtype.Text + RawMessage []byte + BlockHeight int64 } func (q *Queries) InsertCoreReward(ctx context.Context, arg InsertCoreRewardParams) error { @@ -435,7 +435,7 @@ func (q *Queries) InsertCoreReward(ctx context.Context, arg InsertCoreRewardPara arg.RewardID, arg.Name, arg.Amount, - arg.ClaimAuthorities, + arg.RewardsManagerPubkey, arg.RawMessage, arg.BlockHeight, ) @@ -891,6 +891,27 @@ func (q *Queries) InsertRegisteredNode(ctx context.Context, arg InsertRegistered return err } +const insertRewardPool = `-- name: InsertRewardPool :exec +insert into core_reward_pools ( + rewards_manager_pubkey, + authorities +) values ($1, $2) +` + +type InsertRewardPoolParams struct { + RewardsManagerPubkey string + Authorities []string +} + +// Inserts a first-class reward pool created via a CreateRewardPool cometbft +// transaction. The pool's identity IS the Solana reward manager pubkey; +// uniqueness is validated at validate time and same-block collisions +// surface here as a PK violation that fails the second tx in the block. +func (q *Queries) InsertRewardPool(ctx context.Context, arg InsertRewardPoolParams) error { + _, err := q.db.Exec(ctx, insertRewardPool, arg.RewardsManagerPubkey, arg.Authorities) + return err +} + const insertSoundRecording = `-- name: InsertSoundRecording :exec insert into sound_recordings (sound_recording_id, track_id, cid, encoding_details) values ($1, $2, $3, $4) @@ -1054,7 +1075,7 @@ const updateCoreReward = `-- name: UpdateCoreReward :exec update core_rewards set name = $2, amount = $3, - claim_authorities = $4, + rewards_manager_pubkey = $4, raw_message = $5, block_height = $6, updated_at = now() @@ -1062,12 +1083,12 @@ where address = $1 ` type UpdateCoreRewardParams struct { - Address string - Name string - Amount int64 - ClaimAuthorities []string - RawMessage []byte - BlockHeight int64 + Address string + Name string + Amount int64 + RewardsManagerPubkey pgtype.Text + RawMessage []byte + BlockHeight int64 } func (q *Queries) UpdateCoreReward(ctx context.Context, arg UpdateCoreRewardParams) error { @@ -1075,13 +1096,33 @@ func (q *Queries) UpdateCoreReward(ctx context.Context, arg UpdateCoreRewardPara arg.Address, arg.Name, arg.Amount, - arg.ClaimAuthorities, + arg.RewardsManagerPubkey, arg.RawMessage, arg.BlockHeight, ) return err } +const updateRewardPoolAuthorities = `-- name: UpdateRewardPoolAuthorities :exec +update core_reward_pools +set authorities = $2, + updated_at = now() +where rewards_manager_pubkey = $1 +` + +type UpdateRewardPoolAuthoritiesParams struct { + RewardsManagerPubkey string + Authorities []string +} + +// Replaces the authority set on an existing pool wholesale. Used by the +// SetRewardPoolAuthorities finalizer; callers compose the new list themselves +// (current minus the removed key, current plus the added key, etc.). +func (q *Queries) UpdateRewardPoolAuthorities(ctx context.Context, arg UpdateRewardPoolAuthoritiesParams) error { + _, err := q.db.Exec(ctx, updateRewardPoolAuthorities, arg.RewardsManagerPubkey, arg.Authorities) + return err +} + const updateStorageProof = `-- name: UpdateStorageProof :exec update storage_proofs set proof = $1, status = $2 @@ -1120,6 +1161,44 @@ func (q *Queries) UpsertAppState(ctx context.Context, arg UpsertAppStateParams) return err } +const upsertLegacyReplayRewardPool = `-- name: UpsertLegacyReplayRewardPool :exec +insert into core_reward_pools ( + rewards_manager_pubkey, + authorities +) values ($1, $2) +on conflict (rewards_manager_pubkey) do update set + authorities = ( + select array( + select distinct lower(trim(a)) + from unnest(core_reward_pools.authorities || excluded.authorities) as a + where a is not null and trim(a) <> '' + order by 1 + ) + ), + updated_at = now() +` + +type UpsertLegacyReplayRewardPoolParams struct { + RewardsManagerPubkey string + Authorities []string +} + +// Used only by finalizeLegacyCreateReward during block-sync replay of +// legacy-shape CreateReward bytes. Materializes a real-RM pool from the +// legacy reward's inline claim_authorities. +// +// DO UPDATE unions the new authorities with the existing set rather than +// overwriting. This matches the migration backfill, which UNIONs authorities +// across every legacy reward referencing the RM. Without the union, a +// from-genesis-syncing node would end up with +// pool.authorities = last-replayed-reward.authorities, while an in-place- +// upgraded node would have UNION(every reward) — different DB state ⇒ +// different validation outcomes for downstream txs ⇒ apphash divergence. +func (q *Queries) UpsertLegacyReplayRewardPool(ctx context.Context, arg UpsertLegacyReplayRewardPoolParams) error { + _, err := q.db.Exec(ctx, upsertLegacyReplayRewardPool, arg.RewardsManagerPubkey, arg.Authorities) + return err +} + const upsertSlaRollupReport = `-- name: UpsertSlaRollupReport :exec with updated as ( update sla_node_reports diff --git a/pkg/core/server/abci.go b/pkg/core/server/abci.go index 31f2f50b..9ab4e981 100644 --- a/pkg/core/server/abci.go +++ b/pkg/core/server/abci.go @@ -1007,6 +1007,11 @@ func (s *Server) validateBlockTx(ctx context.Context, blockTime time.Time, block s.logger.Error("Invalid block: invalid file upload tx", zap.Error(err)) return false, nil } + case *v1.SignedTransaction_RewardPool: + if err := s.isValidRewardPoolTransaction(ctx, signedTx, blockHeight); err != nil { + s.logger.Error("Invalid block: invalid reward pool tx", zap.Error(err)) + return false, nil + } } return true, nil } @@ -1015,6 +1020,8 @@ func (s *Server) validateV1Transaction(ctx context.Context, currentHeight int64, switch signedTx.Transaction.(type) { case *v1.SignedTransaction_Reward: return s.isValidRewardTransaction(ctx, signedTx, currentHeight) + case *v1.SignedTransaction_RewardPool: + return s.isValidRewardPoolTransaction(ctx, signedTx, currentHeight) default: // For other transaction types, no validation needed during SendTransaction return nil @@ -1046,6 +1053,8 @@ func (s *Server) finalizeTransaction(ctx context.Context, req *abcitypes.Finaliz return s.finalizeRelease(ctx, msg, txHash) case *v1.SignedTransaction_Reward: return s.finalizeRewardTransaction(ctx, req, msg.GetReward(), txHash, sender) + case *v1.SignedTransaction_RewardPool: + return s.finalizeRewardPoolTransaction(ctx, req, msg.GetRewardPool(), txHash, 0) case *v1.SignedTransaction_FileUpload: return s.finalizeFileUpload(ctx, msg, txHash, req.Height) default: diff --git a/pkg/core/server/connect.go b/pkg/core/server/connect.go index aea6f5a7..cf099a63 100644 --- a/pkg/core/server/connect.go +++ b/pkg/core/server/connect.go @@ -24,6 +24,7 @@ import ( storagev1connect "github.com/OpenAudio/go-openaudio/pkg/api/storage/v1/v1connect" "github.com/OpenAudio/go-openaudio/pkg/common" "github.com/OpenAudio/go-openaudio/pkg/core/config" + "github.com/OpenAudio/go-openaudio/pkg/core/db" "github.com/OpenAudio/go-openaudio/pkg/mediorum/server/signature" "github.com/OpenAudio/go-openaudio/pkg/rewards" "github.com/jackc/pgx/v5" @@ -1062,16 +1063,124 @@ func (c *CoreService) GetStatus(ctx context.Context, _ *connect.Request[v1.GetSt // GetRewardAttestation implements v1connect.CoreServiceHandler. // -// TODO: temporarily disabled. Restore the programmatic-reward attestation -// flow (see git history for the previous implementation) once artist-coin -// attestations are ready to be re-enabled. +// Restored from the temporary kill-switch (#215) now that the rotation +// primitive is in place. The authority check uses dbReward.ClaimAuthorities, +// which is sourced from coalesce(p.authorities, '{}') via the LEFT JOIN +// on core_reward_pools — i.e., it reflects the current pool membership +// for the reward's RM, not the stale row-frozen list. So rotating an +// authority out via SetRewardPoolAuthorities immediately revokes their +// ability to authenticate claim attestations. func (c *CoreService) GetRewardAttestation(ctx context.Context, req *connect.Request[v1.GetRewardAttestationRequest]) (*connect.Response[v1.GetRewardAttestationResponse], error) { - return nil, connect.NewError(connect.CodeFailedPrecondition, errors.New("artist-coin attestations are temporarily disabled")) + // Trim user-supplied addresses up front. RewardClaim.Compile + // hex-decodes ethRecipientAddress / claimAuthority and surfaces + // surrounding whitespace as a confusing "failed to decode" error + // deeper in the flow; trimming here lets the same input return + // InvalidArgument at the boundary instead. + ethRecipientAddress := strings.TrimSpace(req.Msg.EthRecipientAddress) + if ethRecipientAddress == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("eth_recipient_address is required")) + } + rewardAddress := strings.TrimSpace(req.Msg.RewardAddress) + if rewardAddress == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("reward_address is required")) + } + specifier := req.Msg.Specifier + if specifier == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("specifier is required")) + } + if len(specifier) > 256 { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("specifier too long")) + } + claimAuthority := strings.TrimSpace(req.Msg.ClaimAuthority) + if claimAuthority == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("claim_authority is required")) + } + signature := req.Msg.Signature + if signature == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("signature is required")) + } + amount := req.Msg.Amount + if amount == 0 { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("amount is required")) + } + rewardId := req.Msg.RewardId + if rewardId == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("reward_id is required")) + } + if req.Msg.AmountDecimals > 18 { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("amount_decimals too large; max 18")) + } + + dbReward, err := c.core.db.GetReward(ctx, rewardAddress) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, errors.New("programmatic reward not found")) + } + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to load reward: %w", err)) + } + + // dbReward.ClaimAuthorities is fed by the pool's current authorities + // via the LEFT JOIN aliasing in GetReward. Building a Reward from it + // gives us pool-gated authentication for free. + claimAuthorities := make([]rewards.ClaimAuthority, len(dbReward.ClaimAuthorities)) + for i, ca := range dbReward.ClaimAuthorities { + claimAuthorities[i] = rewards.ClaimAuthority{Address: ca} + } + reward := rewards.Reward{ + ClaimAuthorities: claimAuthorities, + Amount: uint64(dbReward.Amount), + RewardId: dbReward.RewardID, + Name: dbReward.Name, + } + + // RewardAddress is intentionally NOT set here, even though the proto + // field exists and RewardClaim.Compile supports a 3-piece + // RewardAddress:RewardID:Specifier disbursement_id form. The bytes + // produced by Compile are exactly what the Solana reward manager + // program reconstructs and verifies during evaluate_attestations, + // and that program expects the 2-piece RewardID:Specifier form. + // Adding RewardAddress here would break on-chain signature + // verification, not just change the validator-side signing contract. + // Cross-reward replay protection therefore relies on Specifier being + // disbursement-unique (per recipient + per event), which is the + // existing contract — not on the address binding. + claim := rewards.RewardClaim{ + RecipientEthAddress: ethRecipientAddress, + Amount: amount, + RewardID: req.Msg.RewardId, + Specifier: specifier, + ClaimAuthority: claimAuthority, + Decimals: req.Msg.AmountDecimals, + } + + attester := rewards.NewRewardAttester(c.core.config.EthereumKey, []rewards.Reward{reward}) + + if err := attester.Validate(claim); err != nil { + return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("claim validation failed: %w", err)) + } + if err := attester.Authenticate(claim, signature); err != nil { + return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("authentication failed: %w", err)) + } + _, attestation, err := attester.Attest(claim) + if err != nil { + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("attestation generation failed: %w", err)) + } + + return connect.NewResponse(&v1.GetRewardAttestationResponse{ + Owner: attester.EthereumAddress, + Attestation: attestation, + }), nil } // GetRewards implements v1connect.CoreServiceHandler. func (c *CoreService) GetRewards(ctx context.Context, req *connect.Request[v1.GetRewardsRequest]) (*connect.Response[v1.GetRewardsResponse], error) { - claimAuthority := req.Msg.ClaimAuthority + // Stored authorities are lowercased by rewards.CanonicalAuthorities; the + // underlying GetRewardsByClaimAuthority does a case-sensitive @> array + // containment check. Normalize the caller-supplied address (which is + // often checksum-case from common.PrivKeyToAddress) so lookups match. + // Normalize before the empty check so whitespace-only input is rejected + // as InvalidArgument rather than silently producing an empty result. + claimAuthority := strings.ToLower(strings.TrimSpace(req.Msg.ClaimAuthority)) if claimAuthority == "" { return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("claim_authority required")) } @@ -1154,6 +1263,36 @@ func (c *CoreService) GetReward(ctx context.Context, req *connect.Request[v1.Get return nil, connect.NewError(connect.CodeNotFound, nil) } +// GetRewardPool returns a reward pool keyed by its Solana reward manager +// pubkey. Every pool in core_reward_pools is RM-bound (the backfill +// resolves each pre-existing reward to a real RM via the launchpad +// mapping; CreateRewardPool validates that new pools use a base58 +// 32-byte pubkey). There is no separate synthetic-pool surface to +// filter out. +func (c *CoreService) GetRewardPool(ctx context.Context, req *connect.Request[v1.GetRewardPoolRequest]) (*connect.Response[v1.GetRewardPoolResponse], error) { + // Normalize and shape-validate up front so malformed input returns + // InvalidArgument deterministically instead of falling through to a + // DB lookup that returns NotFound. Use the shape-only validator + // here, NOT the create-time AUDIO denylist: a caller probing + // GetRewardPool(AudioRM) should get a clean NotFound, not a "pool + // reserved" InvalidArgument that hides whether a pool exists. + rewardsManagerPubkey := strings.TrimSpace(req.Msg.RewardsManagerPubkey) + if err := validateRewardsManagerPubkeyShape(rewardsManagerPubkey); err != nil { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } + pool, err := c.core.db.GetRewardPool(ctx, rewardsManagerPubkey) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("pool not found for rewards_manager_pubkey: %s", rewardsManagerPubkey)) + } + return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to get pool: %w", err)) + } + return connect.NewResponse(&v1.GetRewardPoolResponse{ + RewardsManagerPubkey: pool.RewardsManagerPubkey, + Authorities: pool.Authorities, + }), nil +} + // GetERN implements v1connect.CoreServiceHandler. func (c *CoreService) GetERN(ctx context.Context, req *connect.Request[v1.GetERNRequest]) (*connect.Response[v1.GetERNResponse], error) { address := req.Msg.Address @@ -1639,12 +1778,16 @@ func (c *CoreService) GetSlashAttestations(ctx context.Context, req *connect.Req // gatherEligibleSenderAddresses returns the union of registered validator // eth addresses and anti-abuse oracle eth addresses. This is the set the -// rewards-manager program treats as eligible senders: add attestations are -// only signed for addresses in this set, delete attestations are only signed -// for addresses NOT in this set. +// rewards-manager program treats as eligible senders for RMs that are NOT +// managed by the pool primitive: add attestations are only signed for +// addresses in this set, delete attestations are only signed for addresses +// NOT in this set. // // Validators come from the local consensus-populated core_validators table. // AAOs come from the L1 EthRewardsManager contract via the eth-bridge sync. +// +// Pool-managed RMs (those with a row in core_reward_pools) bypass this and +// gate on pool.authorities instead — see senderGateForRM. func (c *CoreService) gatherEligibleSenderAddresses(ctx context.Context) ([]string, error) { validators, err := c.core.db.GetAllEthAddressesOfRegisteredNodes(ctx) if err != nil { @@ -1657,27 +1800,99 @@ func (c *CoreService) gatherEligibleSenderAddresses(ctx context.Context) ([]stri return append(validators, aaos...), nil } +// ErrSenderGateUnknownRM is returned by senderGateForRM when the requested +// RM has no core_reward_pools row AND is not the configured AUDIO RM. +// Callers should map it to connect.CodeInvalidArgument. +var ErrSenderGateUnknownRM = errors.New("rewards_manager_pubkey has no pool and is not AUDIO") + +// senderGateForRM resolves which gating regime applies to a given Solana +// reward manager pubkey: +// +// - If a row exists in core_reward_pools for the requested RM, the pool +// is the source of truth: addAttestation iff addr ∈ pool.authorities; +// deleteAttestation iff addr ∉ pool.authorities. This is the rotation +// path — once OAP rotates a key out of the pool, validators can be +// asked to deregister it from Solana, and once a new key is rotated in, +// validators can register it. +// +// - If no pool exists AND the RM is the configured AUDIO RM, fall through +// to the legacy validator/AAO trust set (see +// gatherEligibleSenderAddresses). AUDIO is the only RM that may use +// this path: no pool is ever created for it (validateCreateRewardPool +// refuses), so AUDIO attestation authority remains the network-wide +// trust set. +// +// - If no pool exists AND the RM is NOT AUDIO, return +// ErrSenderGateUnknownRM. We deliberately refuse to fall through — +// pools are now the only authorization mechanism for non-AUDIO RMs, +// and a quietly-permissive fallback would re-open the exact gap the +// pool primitive is meant to close (any caller could request +// validator-signed attestations for an arbitrary unknown RM). +// +// Returns (pool, true, nil) for pool gating, (nil, false, nil) for the +// AUDIO legacy path, ErrSenderGateUnknownRM for any other no-pool RM, and +// other errors only on real DB failures (transient errors do NOT silently +// fall through to validator/AAO — that would let a temporary blip +// downgrade the gate). +func (c *CoreService) senderGateForRM(ctx context.Context, rmPubkey string) (*db.CoreRewardPool, bool, error) { + pool, err := c.core.db.GetRewardPool(ctx, rmPubkey) + switch { + case err == nil: + return &pool, true, nil + case !errors.Is(err, pgx.ErrNoRows): + return nil, false, fmt.Errorf("failed to load pool for RM %s: %w", rmPubkey, err) + } + if audioRM := strings.TrimSpace(config.AudioRewardsManagerPubkey()); audioRM != "" && rmPubkey == audioRM { + return nil, false, nil + } + return nil, false, ErrSenderGateUnknownRM +} + // GetRewardSenderAttestation implements v1connect.CoreServiceHandler. +// +// Dispatches by RM: +// - If a pool exists for the requested RM, sign iff address ∈ pool.authorities. +// - If no pool exists and the RM is the configured AUDIO RM, sign iff +// address ∈ validator/AAO trust set (legacy AUDIO path). +// - Otherwise (no pool, not AUDIO), refuse — there is no authorization +// mechanism to consult. func (c *CoreService) GetRewardSenderAttestation(ctx context.Context, req *connect.Request[v1.GetRewardSenderAttestationRequest]) (*connect.Response[v1.GetRewardSenderAttestationResponse], error) { - address := req.Msg.Address + address := strings.TrimSpace(req.Msg.Address) if address == "" { return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address is required")) } - rewardsManagerPubkey := req.Msg.RewardsManagerPubkey - if rewardsManagerPubkey == "" { - return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("reward manager pubkey is required")) + rewardsManagerPubkey := strings.TrimSpace(req.Msg.RewardsManagerPubkey) + if err := validateRewardsManagerPubkeyShape(rewardsManagerPubkey); err != nil { + // Shape-validate up front so a malformed pubkey returns + // InvalidArgument with a clear message instead of falling through + // to senderGateForRM and surfacing as ErrSenderGateUnknownRM + // (which is misleading — it's invalid input, not an unknown RM). + return nil, connect.NewError(connect.CodeInvalidArgument, err) } - eligible, err := c.gatherEligibleSenderAddresses(ctx) + pool, isPoolGated, err := c.senderGateForRM(ctx, rewardsManagerPubkey) if err != nil { + if errors.Is(err, ErrSenderGateUnknownRM) { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } return nil, connect.NewError(connect.CodeInternal, err) } - if !slices.ContainsFunc(eligible, func(eth string) bool { - return strings.EqualFold(eth, address) - }) { - return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address not a registered validator or anti-abuse oracle")) + if isPoolGated { + if !slices.Contains(pool.Authorities, strings.ToLower(address)) { + return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("address not in pool.authorities for RM %s", rewardsManagerPubkey)) + } + } else { + eligible, err := c.gatherEligibleSenderAddresses(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeInternal, err) + } + if !slices.ContainsFunc(eligible, func(eth string) bool { + return strings.EqualFold(eth, address) + }) { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address not a registered validator or anti-abuse oracle")) + } } owner, attestation, err := rewards.GetCreateSenderAttestation(c.core.config.EthereumKey, &rewards.CreateSenderAttestationParams{ @@ -1696,26 +1911,52 @@ func (c *CoreService) GetRewardSenderAttestation(ctx context.Context, req *conne } // GetDeleteRewardSenderAttestation implements v1connect.CoreServiceHandler. +// +// Dispatches by RM: +// - If a pool exists for the requested RM, sign iff address ∉ pool.authorities. +// This is the rotation-out signal: OAP has already removed the key from the +// pool, and validators are catching Solana up. +// - If no pool exists and the RM is the configured AUDIO RM, sign iff +// address is NOT in the validator/AAO trust set (legacy AUDIO path). +// - Otherwise (no pool, not AUDIO), refuse — there is no authorization +// mechanism to consult. func (c *CoreService) GetDeleteRewardSenderAttestation(ctx context.Context, req *connect.Request[v1.GetDeleteRewardSenderAttestationRequest]) (*connect.Response[v1.GetDeleteRewardSenderAttestationResponse], error) { - address := req.Msg.Address + address := strings.TrimSpace(req.Msg.Address) if address == "" { return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address is required")) } - rewardsManagerPubkey := req.Msg.RewardsManagerPubkey - if rewardsManagerPubkey == "" { - return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("reward manager pubkey is required")) + rewardsManagerPubkey := strings.TrimSpace(req.Msg.RewardsManagerPubkey) + if err := validateRewardsManagerPubkeyShape(rewardsManagerPubkey); err != nil { + // Shape-validate up front so a malformed pubkey returns + // InvalidArgument with a clear message instead of falling through + // to senderGateForRM and surfacing as ErrSenderGateUnknownRM + // (which is misleading — it's invalid input, not an unknown RM). + return nil, connect.NewError(connect.CodeInvalidArgument, err) } - eligible, err := c.gatherEligibleSenderAddresses(ctx) + pool, isPoolGated, err := c.senderGateForRM(ctx, rewardsManagerPubkey) if err != nil { + if errors.Is(err, ErrSenderGateUnknownRM) { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } return nil, connect.NewError(connect.CodeInternal, err) } - if slices.ContainsFunc(eligible, func(eth string) bool { - return strings.EqualFold(eth, address) - }) { - return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address is a registered validator or anti-abuse oracle")) + if isPoolGated { + if slices.Contains(pool.Authorities, strings.ToLower(address)) { + return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("address is still a current authority of the pool for RM %s; rotate it out first via SetRewardPoolAuthorities", rewardsManagerPubkey)) + } + } else { + eligible, err := c.gatherEligibleSenderAddresses(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeInternal, err) + } + if slices.ContainsFunc(eligible, func(eth string) bool { + return strings.EqualFold(eth, address) + }) { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("address is a registered validator or anti-abuse oracle")) + } } owner, attestation, err := rewards.GetDeleteSenderAttestation(c.core.config.EthereumKey, &rewards.DeleteSenderAttestationParams{ diff --git a/pkg/core/server/reward_pools.go b/pkg/core/server/reward_pools.go new file mode 100644 index 00000000..cab76291 --- /dev/null +++ b/pkg/core/server/reward_pools.go @@ -0,0 +1,340 @@ +package server + +import ( + "context" + "crypto/ed25519" + "errors" + "fmt" + "slices" + "strings" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/OpenAudio/go-openaudio/pkg/common" + "github.com/OpenAudio/go-openaudio/pkg/core/config" + "github.com/OpenAudio/go-openaudio/pkg/core/db" + "github.com/OpenAudio/go-openaudio/pkg/rewards" + abcitypes "github.com/cometbft/cometbft/abci/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v5" + "github.com/mr-tron/base58/base58" + "google.golang.org/protobuf/proto" +) + +// solanaPubkeyByteLen is the wire-level size of a Solana pubkey (32 bytes, +// base58-encoded). Used to validate that a rewards_manager_pubkey supplied +// to CreateRewardPool / CreateReward / SetRewardPoolAuthorities is the +// shape of an actual on-chain reward manager account, not an arbitrary +// caller-chosen string. +const solanaPubkeyByteLen = 32 + +// ErrRewardPoolOwnerSignatureInvalid signals that the envelope's +// rm_owner_signature did not verify against the body's claimed RM pubkey. +var ErrRewardPoolOwnerSignatureInvalid = errors.New("reward pool owner signature invalid") + +// verifyRewardPoolOwnerSignature checks that the supplied ed25519 +// signature, produced by the keypair whose public key is rmPubkey, +// covers bodyBytes. Returns nil iff the signature is valid; otherwise +// returns a wrapped ErrRewardPoolOwnerSignatureInvalid. +// +// The verification key IS rmPubkey, decoded from base58. There is no +// trusted-key registry: the body claims an RM, the RM IS its ed25519 +// verification key, and possession of the matching secret key proves +// authorization to register a pool for that RM. The launchpad relay +// derives both the keypair and the per-mint claim authority eth address +// from (launchpadDeterministicSecret, mint), so the launchpad can +// always produce this signature; an attacker who lacks the secret +// cannot. +// +// bodyBytes are the deterministic-protobuf marshaling of RewardPoolBody — +// the same bytes the secp256k1 envelope signature covers. This means +// fields present in the body, including deadline_block_height, are +// implicitly covered by both signatures, defeating stale-signature replay +// without a separate signed-string format. +func verifyRewardPoolOwnerSignature(rmPubkey string, bodyBytes, signature []byte) error { + if len(signature) != ed25519.SignatureSize { + return fmt.Errorf("%w: signature length is %d, want %d", ErrRewardPoolOwnerSignatureInvalid, len(signature), ed25519.SignatureSize) + } + pubkeyBytes, err := base58.Decode(rmPubkey) + if err != nil { + return fmt.Errorf("%w: rewards_manager_pubkey is not valid base58: %v", ErrRewardPoolOwnerSignatureInvalid, err) + } + if len(pubkeyBytes) != ed25519.PublicKeySize { + return fmt.Errorf("%w: rewards_manager_pubkey decodes to %d bytes, want %d", ErrRewardPoolOwnerSignatureInvalid, len(pubkeyBytes), ed25519.PublicKeySize) + } + if !ed25519.Verify(ed25519.PublicKey(pubkeyBytes), bodyBytes, signature) { + return ErrRewardPoolOwnerSignatureInvalid + } + return nil +} + +// isValidRewardPoolTransaction is the entry point for both CheckTx and +// block validation. Signatures + deadline live on the envelope; we +// recover the secp256k1 signer once here, then dispatch to per-action +// validators that handle the rest (including the ed25519 +// rm_owner_signature for Create). +func (s *Server) isValidRewardPoolTransaction(ctx context.Context, signedTx *corev1.SignedTransaction, blockHeight int64) error { + envelope := signedTx.GetRewardPool() + if envelope == nil || envelope.Body == nil { + return fmt.Errorf("%w: reward pool message body is nil", ErrRewardMessageValidation) + } + + signer, err := s.recoverDeadlinedSigner(blockHeight, envelope.Body.DeadlineBlockHeight, envelope.Body, envelope.Signature) + if err != nil { + return fmt.Errorf("reward pool validation failed: %w", err) + } + + switch action := envelope.Body.Action.(type) { + case *corev1.RewardPoolBody_Create: + return s.validateCreateRewardPool(ctx, envelope, action.Create, signer) + case *corev1.RewardPoolBody_SetAuthorities: + return s.validateSetRewardPoolAuthorities(ctx, action.SetAuthorities, signer) + default: + return fmt.Errorf("%w: unsupported reward pool action type", ErrRewardMessageValidation) + } +} + +// validateCreateRewardPool runs two independent authorization checks +// alongside the structural validation: +// +// - envelope.rm_owner_signature (ed25519, verified against +// msg.rewards_manager_pubkey): proves the caller controls the +// RM keypair. Defeats frontrunning by an observer who sees the RM +// pubkey on Solana but lacks the launchpad's deterministic secret. +// - envelope signer (secp256k1, recovered from envelope.signature) ∈ +// msg.authorities: keeps the "you can't create a pool you have no +// membership in" property. Operationally the launchpad relay holds +// both keys and uses the per-mint claim authority eth key as the +// envelope signer and as the only initial authority. +// +// Both signatures cover the same body bytes — the secp256k1 path uses +// common.ProtoRecover (which marshals deterministically); the ed25519 +// path verifies against common.ProtoSignableBytes(body) directly. body +// covers deadline_block_height and the action oneof, so stale-deadline +// replay is blocked for both signatures together. Cross-chain replay +// isn't a threat: each environment's launchpad uses a different +// deterministic secret, so the same rewards_manager_pubkey cannot exist +// on more than one chain. +func (s *Server) validateCreateRewardPool(ctx context.Context, envelope *corev1.RewardPoolMessage, msg *corev1.CreateRewardPool, signer string) error { + if err := validateRewardsManagerPubkey(msg.RewardsManagerPubkey); err != nil { + return err + } + if err := validateAuthorityList(msg.Authorities); err != nil { + return err + } + bodyBytes, err := common.ProtoSignableBytes(envelope.Body) + if err != nil { + return fmt.Errorf("%w: marshal body for rm signature: %v", ErrRewardPoolOwnerSignatureInvalid, err) + } + if err := verifyRewardPoolOwnerSignature(msg.RewardsManagerPubkey, bodyBytes, envelope.RmOwnerSignature); err != nil { + return err + } + canonical := rewards.CanonicalAuthorities(msg.Authorities) + if !slices.Contains(canonical, strings.ToLower(strings.TrimSpace(signer))) { + return fmt.Errorf("%w: signer %s not in initial authorities", ErrRewardUnauthorized, signer) + } + if _, err := s.db.GetRewardPool(ctx, msg.RewardsManagerPubkey); err == nil { + return fmt.Errorf("%w: pool %s already exists", ErrRewardMessageValidation, msg.RewardsManagerPubkey) + } else if !errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("failed to check pool existence: %w", err) + } + return nil +} + +// validateAuthorityList enforces that an authority list is non-empty and +// every entry is a valid eth address. Without this, a current authority can +// rotate the pool to ["not-an-address"], which passes the canonicalization +// pipeline but leaves the pool with no key that can ever satisfy +// checkPoolAuthorization — every reward attached to the pool becomes +// permanently unclaimable. +func validateAuthorityList(addrs []string) error { + if len(addrs) == 0 { + return fmt.Errorf("%w: at least one authority is required", ErrRewardMessageValidation) + } + for _, a := range addrs { + if !ethcommon.IsHexAddress(strings.TrimSpace(a)) { + return fmt.Errorf("%w: %q is not a valid eth address", ErrRewardMessageValidation, a) + } + } + return nil +} + +// validateRewardsManagerPubkeyShape checks the wire shape of a Solana +// reward manager pubkey: non-empty, no surrounding whitespace, base58- +// decodable, exactly 32 bytes. Pure shape validation — no policy. Use +// from read paths and rotation paths where the AUDIO RM is a legitimate +// input that should round-trip cleanly (read paths return NotFound; +// sender-gate falls back to validator/AAO). +func validateRewardsManagerPubkeyShape(pubkey string) error { + if pubkey == "" { + return fmt.Errorf("%w: rewards_manager_pubkey is required", ErrRewardMessageValidation) + } + if pubkey != strings.TrimSpace(pubkey) { + return fmt.Errorf("%w: rewards_manager_pubkey must not have surrounding whitespace", ErrRewardMessageValidation) + } + bytes, err := base58.Decode(pubkey) + if err != nil { + return fmt.Errorf("%w: rewards_manager_pubkey is not valid base58: %v", ErrRewardMessageValidation, err) + } + if len(bytes) != solanaPubkeyByteLen { + return fmt.Errorf("%w: rewards_manager_pubkey must decode to %d bytes; got %d", ErrRewardMessageValidation, solanaPubkeyByteLen, len(bytes)) + } + return nil +} + +// validateRewardsManagerPubkey is the WRITE-path validator: shape + +// the AUDIO reserved-RM denylist. Use from CreateRewardPool and +// CreateReward. Allowing a pool to be created for the AUDIO RM would +// shift AUDIO sender attestations to pool-controlled authorities, +// defeating the AUDIO trust model — AUDIO is intentionally governed by +// the network-wide validator/AAO trust set in senderGateForRM (the +// gate falls back to validator/AAO when the configured AUDIO RM has +// no pool). +// +// First-class pools must use a real RM pubkey because senderGateForRM +// uses the same value to bind the pool↔RM. The 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. +func validateRewardsManagerPubkey(pubkey string) error { + if err := validateRewardsManagerPubkeyShape(pubkey); err != nil { + return err + } + // Trim defensively on the configured side: if these per-env + // constants ever start being populated from env vars or config, + // surrounding whitespace would silently disable the check. + if audioRM := strings.TrimSpace(config.AudioRewardsManagerPubkey()); audioRM != "" && pubkey == audioRM { + return fmt.Errorf("%w: rewards_manager_pubkey is reserved (AUDIO); pools cannot be created for it", ErrRewardMessageValidation) + } + return nil +} + +// validateSetRewardPoolAuthorities: rewards_manager_pubkey must be a real +// Solana RM pubkey (defense-in-depth — every pool created via +// CreateRewardPool already passed this check, but enforcing it here too +// closes any historical or future path that might have inserted a +// non-RM-shaped pool key); signer must be in the *current* pool +// authorities; the new list must be non-empty and contain only valid eth +// addresses (otherwise the pool can be rotated into a permanently-orphaned +// state). +func (s *Server) validateSetRewardPoolAuthorities(ctx context.Context, msg *corev1.SetRewardPoolAuthorities, signer string) error { + // Shape-only here, not the AUDIO denylist: SetRewardPoolAuthorities + // targets an existing pool, and AUDIO has no pool by construction + // (validateCreateRewardPool refuses), so checkPoolAuthorization + // surfaces the AUDIO case as the more accurate "pool not found" + // rather than the create-time "is reserved" message. + if err := validateRewardsManagerPubkeyShape(msg.RewardsManagerPubkey); err != nil { + return err + } + if err := validateAuthorityList(msg.Authorities); err != nil { + return err + } + return s.checkPoolAuthorization(ctx, s.db, msg.RewardsManagerPubkey, signer) +} + +// finalizeRewardPoolTransaction is invoked after a tx is included in a block. +// Signatures were already verified at validate time; we re-check here as +// defense-in-depth because the finalize path is its own consensus boundary +// (block-sync replay does not re-run ProcessProposal / CheckTx). +func (s *Server) finalizeRewardPoolTransaction(ctx context.Context, req *abcitypes.FinalizeBlockRequest, envelope *corev1.RewardPoolMessage, txhash string, messageIndex int64) (proto.Message, error) { + if envelope == nil || envelope.Body == nil { + return nil, fmt.Errorf("tx: %s, message index: %d, reward pool message body not found", txhash, messageIndex) + } + signer, err := s.recoverDeadlinedSigner(req.Height, envelope.Body.DeadlineBlockHeight, envelope.Body, envelope.Signature) + if err != nil { + return nil, errors.Join(ErrRewardMessageFinalization, fmt.Errorf("signer recovery: %w", err)) + } + + switch action := envelope.Body.Action.(type) { + case *corev1.RewardPoolBody_Create: + if err := s.finalizeCreateRewardPool(ctx, envelope, action.Create, signer); err != nil { + return nil, errors.Join(ErrRewardMessageFinalization, err) + } + case *corev1.RewardPoolBody_SetAuthorities: + if err := s.finalizeSetRewardPoolAuthorities(ctx, action.SetAuthorities, signer); err != nil { + return nil, errors.Join(ErrRewardMessageFinalization, err) + } + default: + return nil, fmt.Errorf("tx: %s, message index: %d, unsupported reward pool action type", txhash, messageIndex) + } + return envelope, nil +} + +// finalizeCreateRewardPool: pool address == rewards_manager_pubkey. Same-RM +// in-block collisions surface as a PK violation from InsertRewardPool, which +// fails the tx (block continues; no chain crash). +// +// Re-validates the message shape at finalize time as defense-in-depth: +// block-sync replay calls FinalizeBlock without re-running ProcessProposal, +// so any malformed bytes that ever reached the chain would skip the +// validate-time checks. Repeating the same shape + signer-membership +// validation here keeps the post-replay state identical to what the live +// validate path produces. +func (s *Server) finalizeCreateRewardPool(ctx context.Context, envelope *corev1.RewardPoolMessage, msg *corev1.CreateRewardPool, signer string) error { + if err := validateRewardsManagerPubkey(msg.RewardsManagerPubkey); err != nil { + return err + } + if err := validateAuthorityList(msg.Authorities); err != nil { + return err + } + bodyBytes, err := common.ProtoSignableBytes(envelope.Body) + if err != nil { + return fmt.Errorf("%w: marshal body for rm signature: %v", ErrRewardPoolOwnerSignatureInvalid, err) + } + if err := verifyRewardPoolOwnerSignature(msg.RewardsManagerPubkey, bodyBytes, envelope.RmOwnerSignature); err != nil { + return err + } + canonical := rewards.CanonicalAuthorities(msg.Authorities) + if !slices.Contains(canonical, strings.ToLower(strings.TrimSpace(signer))) { + return fmt.Errorf("%w: signer %s not in initial authorities", ErrRewardUnauthorized, signer) + } + return s.getDb().InsertRewardPool(ctx, db.InsertRewardPoolParams{ + RewardsManagerPubkey: msg.RewardsManagerPubkey, + Authorities: canonical, + }) +} + +// finalizeSetRewardPoolAuthorities re-checks signer authorization against +// post-prior-tx state via s.getDb(), guarding against same-block ordering +// where an earlier tx rotates the signer out before this one runs. +// +// Also re-runs the same shape validation as the live validate path +// (defense-in-depth, matching finalizeCreateRewardPool): block-sync +// replay calls FinalizeBlock without re-running ProcessProposal, so any +// malformed bytes that ever reached the chain would skip the validate- +// time checks. Repeating them here keeps the post-replay state identical +// to live and prevents an orphaned/empty-authority pool from being +// written. +func (s *Server) finalizeSetRewardPoolAuthorities(ctx context.Context, msg *corev1.SetRewardPoolAuthorities, signer string) error { + if err := validateRewardsManagerPubkeyShape(msg.RewardsManagerPubkey); err != nil { + return err + } + if err := validateAuthorityList(msg.Authorities); err != nil { + return err + } + if err := s.checkPoolAuthorization(ctx, s.getDb(), msg.RewardsManagerPubkey, signer); err != nil { + return err + } + return s.getDb().UpdateRewardPoolAuthorities(ctx, db.UpdateRewardPoolAuthoritiesParams{ + RewardsManagerPubkey: msg.RewardsManagerPubkey, + Authorities: rewards.CanonicalAuthorities(msg.Authorities), + }) +} + +// checkPoolAuthorization fetches the pool from the supplied queries handle +// (s.db at validate time, s.getDb() at finalize time) and verifies signer is +// in the current authority set. Used by both validate and finalize paths so +// the rule lives in one place. +func (s *Server) checkPoolAuthorization(ctx context.Context, q *db.Queries, rewardsManagerPubkey, signer string) error { + pool, err := q.GetRewardPool(ctx, rewardsManagerPubkey) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("%w: pool %s not found", ErrRewardMessageValidation, rewardsManagerPubkey) + } + return fmt.Errorf("failed to load pool %s: %w", rewardsManagerPubkey, err) + } + if !slices.Contains(pool.Authorities, strings.ToLower(strings.TrimSpace(signer))) { + return fmt.Errorf("%w: signer %s not authorized for pool %s", ErrRewardUnauthorized, signer, rewardsManagerPubkey) + } + return nil +} + diff --git a/pkg/core/server/reward_pools_test.go b/pkg/core/server/reward_pools_test.go new file mode 100644 index 00000000..510faff5 --- /dev/null +++ b/pkg/core/server/reward_pools_test.go @@ -0,0 +1,168 @@ +package server + +import ( + "crypto/ed25519" + "crypto/rand" + "errors" + "strings" + "testing" + + "github.com/OpenAudio/go-openaudio/pkg/core/config" + "github.com/mr-tron/base58/base58" +) + +// TestValidateRewardsManagerPubkey_BaseChecks covers shape rejection: +// empty, surrounding whitespace, non-base58, and wrong byte length. +// The AUDIO denylist is exercised separately by +// TestValidateRewardsManagerPubkey_AudioDenylist. +func TestValidateRewardsManagerPubkey_BaseChecks(t *testing.T) { + good := freshSolanaPubkeyForTest(t) + + cases := []struct { + name string + pubkey string + wantErr string + }{ + {"empty", "", "is required"}, + {"leading whitespace", " " + good, "whitespace"}, + {"trailing whitespace", good + " ", "whitespace"}, + {"non-base58", "this!is@not%base58", "not valid base58"}, + {"too few bytes", base58.Encode([]byte("short")), "must decode to 32 bytes"}, + {"good", good, ""}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := validateRewardsManagerPubkey(tc.pubkey) + if tc.wantErr == "" { + if err != nil { + t.Fatalf("expected pass; got %v", err) + } + return + } + if err == nil { + t.Fatalf("expected error containing %q; got nil", tc.wantErr) + } + if !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("error %q does not contain %q", err.Error(), tc.wantErr) + } + }) + } +} + +// TestValidateRewardsManagerPubkey_AudioDenylist verifies that a CreateRewardPool +// targeting the configured AUDIO RM is refused. We swap in a test-only +// AUDIO RM via the per-env var (this test runs in the default 'prod' +// runtime environment per GetRuntimeEnvironment's fallback), so we set +// ProdAudioRewardsManagerPubkey for the duration of the test. +func TestValidateRewardsManagerPubkey_AudioDenylist(t *testing.T) { + // Two distinct, well-formed pubkeys: one will be the configured AUDIO RM + // (denylisted), the other an arbitrary launchpad RM (allowed). + audioRM := freshSolanaPubkeyForTest(t) + otherRM := freshSolanaPubkeyForTest(t) + + // Save and restore so the test is hermetic. + prevDev := config.DevAudioRewardsManagerPubkey + prevProd := config.ProdAudioRewardsManagerPubkey + defer func() { + config.DevAudioRewardsManagerPubkey = prevDev + config.ProdAudioRewardsManagerPubkey = prevProd + }() + // Set both so the test passes regardless of which env happens to be + // resolved (default is "prod" per GetRuntimeEnvironment). + config.DevAudioRewardsManagerPubkey = audioRM + config.ProdAudioRewardsManagerPubkey = audioRM + + if err := validateRewardsManagerPubkey(audioRM); err == nil { + t.Fatalf("expected AUDIO RM to be rejected by denylist") + } else if !strings.Contains(err.Error(), "reserved") { + t.Fatalf("expected 'reserved' error; got %v", err) + } + + if err := validateRewardsManagerPubkey(otherRM); err != nil { + t.Fatalf("expected non-AUDIO RM to pass; got %v", err) + } +} + +func freshSolanaPubkeyForTest(t *testing.T) string { + t.Helper() + var b [32]byte + if _, err := rand.Read(b[:]); err != nil { + t.Fatalf("rand: %v", err) + } + return base58.Encode(b[:]) +} + +// TestVerifyRewardPoolOwnerSignature exercises the ed25519 proof-of-RM- +// keypair-possession that gates CreateRewardPool. Frontrunning defense +// rests on this check: the verification key IS the rm_pubkey, so +// possession of the matching secret is the only way to produce a valid +// signature over the body bytes. +func TestVerifyRewardPoolOwnerSignature(t *testing.T) { + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("ed25519.GenerateKey: %v", err) + } + rmPubkey := base58.Encode(pub) + + // Foreign keypair representing an attacker who controls a different + // RM and tries to reuse their signature against ours. + _, foreignPriv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("ed25519.GenerateKey foreign: %v", err) + } + + // The validator hashes body bytes for verification — we don't care + // what those bytes are in this unit test, just that the (sig, key, + // bytes) triple matches. + bodyBytes := []byte("body-marshal-bytes-stand-in") + + t.Run("valid signature passes", func(t *testing.T) { + sig := ed25519.Sign(priv, bodyBytes) + if err := verifyRewardPoolOwnerSignature(rmPubkey, bodyBytes, sig); err != nil { + t.Fatalf("expected valid signature to verify; got %v", err) + } + }) + + t.Run("signature signed by foreign keypair fails", func(t *testing.T) { + // Foreign secret signs the same bytes; verify against our rmPubkey + // must reject — verification key doesn't match the signing key. + sig := ed25519.Sign(foreignPriv, bodyBytes) + err := verifyRewardPoolOwnerSignature(rmPubkey, bodyBytes, sig) + if !errors.Is(err, ErrRewardPoolOwnerSignatureInvalid) { + t.Fatalf("expected ErrRewardPoolOwnerSignatureInvalid; got %v", err) + } + }) + + t.Run("signature over different body bytes fails", func(t *testing.T) { + sig := ed25519.Sign(priv, []byte("different-body")) + err := verifyRewardPoolOwnerSignature(rmPubkey, bodyBytes, sig) + if !errors.Is(err, ErrRewardPoolOwnerSignatureInvalid) { + t.Fatalf("expected mismatched-body signature rejected; got %v", err) + } + }) + + t.Run("malformed signature length fails", func(t *testing.T) { + err := verifyRewardPoolOwnerSignature(rmPubkey, bodyBytes, []byte("too-short")) + if !errors.Is(err, ErrRewardPoolOwnerSignatureInvalid) { + t.Fatalf("expected short-signature rejected; got %v", err) + } + }) + + t.Run("non-base58 rm_pubkey fails", func(t *testing.T) { + sig := ed25519.Sign(priv, bodyBytes) + err := verifyRewardPoolOwnerSignature("not!base58", bodyBytes, sig) + if !errors.Is(err, ErrRewardPoolOwnerSignatureInvalid) { + t.Fatalf("expected invalid-pubkey rejected; got %v", err) + } + }) + + t.Run("rm_pubkey wrong byte length fails", func(t *testing.T) { + shortPubkey := base58.Encode([]byte("short")) + sig := ed25519.Sign(priv, bodyBytes) + err := verifyRewardPoolOwnerSignature(shortPubkey, bodyBytes, sig) + if !errors.Is(err, ErrRewardPoolOwnerSignatureInvalid) { + t.Fatalf("expected wrong-length rm_pubkey rejected; got %v", err) + } + }) +} diff --git a/pkg/core/server/rewards.go b/pkg/core/server/rewards.go index 0062281c..f7e4a764 100644 --- a/pkg/core/server/rewards.go +++ b/pkg/core/server/rewards.go @@ -2,10 +2,10 @@ package server import ( "context" - "encoding/hex" "errors" "fmt" "net/http" + "slices" "strconv" "strings" @@ -14,6 +14,8 @@ import ( "github.com/OpenAudio/go-openaudio/pkg/core/db" "github.com/OpenAudio/go-openaudio/pkg/rewards" abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" "github.com/labstack/echo/v4" "google.golang.org/protobuf/proto" ) @@ -91,75 +93,94 @@ var ( ) func (s *Server) isValidRewardTransaction(ctx context.Context, signedTx *corev1.SignedTransaction, blockHeight int64) error { - rewardMsg := signedTx.GetReward() - if rewardMsg == nil { + 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) + } + return fmt.Errorf("%w: reward message body is nil", ErrRewardMessageValidation) + } + + signer, err := s.recoverDeadlinedSigner(blockHeight, envelope.Body.DeadlineBlockHeight, envelope.Body, envelope.Signature) + if err != nil { + return fmt.Errorf("reward validation failed: %w", err) + } - switch action := rewardMsg.Action.(type) { - case *corev1.RewardMessage_Create: - return s.validateCreateReward(ctx, action.Create, blockHeight) - case *corev1.RewardMessage_Delete: - return s.validateDeleteReward(ctx, action.Delete, blockHeight) + switch action := envelope.Body.Action.(type) { + case *corev1.RewardBody_Create: + return s.validateCreateReward(ctx, action.Create, signer) + case *corev1.RewardBody_Delete: + return s.validateDeleteReward(ctx, action.Delete, signer) default: return fmt.Errorf("%w: unsupported reward action type", ErrRewardMessageValidation) } } -func (s *Server) validateCreateReward(_ context.Context, createReward *corev1.CreateReward, blockHeight int64) error { - signatureData := common.CreateDeterministicCreateRewardData(createReward) - _, err := s.validateRewardSignature(blockHeight, createReward.Signature, createReward.DeadlineBlockHeight, signatureData) - if err != nil { - return fmt.Errorf("create reward validation failed: %w", err) - } - return nil +func (s *Server) validateCreateReward(ctx context.Context, createReward *corev1.CreateReward, signer string) error { + // New CreateReward requires a first-class pool. The pool is identified + // by the Solana reward manager pubkey, and only pool members can issue + // rewards under that RM. Without this binding, a member of one pool + // could issue rewards under any other pool's RM and inherit its + // attestation rights — which is exactly what the per-RM + // sender-attestation gate is designed to prevent. + // + // The legacy permissionless path (inline claim_authorities, no RM) is + // preserved only for historical replay via the wire-compat layer; new + // live txs must specify rewards_manager_pubkey. + if err := validateRewardsManagerPubkey(createReward.RewardsManagerPubkey); err != nil { + return err + } + return s.checkPoolAuthorization(ctx, s.db, createReward.RewardsManagerPubkey, signer) } -func (s *Server) validateDeleteReward(ctx context.Context, deleteReward *corev1.DeleteReward, blockHeight int64) error { - signatureData := common.CreateDeterministicDeleteRewardData(deleteReward) - signer, err := s.validateRewardSignature(blockHeight, deleteReward.Signature, deleteReward.DeadlineBlockHeight, signatureData) - if err != nil { - return fmt.Errorf("delete reward validation failed: %w", err) - } - +func (s *Server) validateDeleteReward(ctx context.Context, deleteReward *corev1.DeleteReward, signer string) error { existingReward, err := s.db.GetReward(ctx, deleteReward.Address) if err != nil { - return fmt.Errorf("failed to get existing reward for validation: %w", err) - } - - authorized := false - for _, auth := range existingReward.ClaimAuthorities { - if strings.EqualFold(auth, signer) { - authorized = true - break + if errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("%w: reward %s not found", ErrRewardMessageValidation, deleteReward.Address) } + return fmt.Errorf("failed to get existing reward for validation: %w", err) } - if !authorized { + if !slices.Contains(existingReward.ClaimAuthorities, strings.ToLower(strings.TrimSpace(signer))) { return fmt.Errorf("%w: signer %s not authorized to delete reward %s", ErrRewardUnauthorized, signer, deleteReward.Address) } - return nil } -// validateRewardSignature validates the signature and expiry for reward messages -func (s *Server) validateRewardSignature(currentHeight int64, signature string, deadlineHeight int64, signatureData string) (string, error) { - // Check expiry +// recoverDeadlinedSigner enforces the message's deadline against the current +// block height and recovers the eth address that produced signature over +// the deterministic-protobuf marshaling of msg. msg is expected to be a +// signed body (RewardBody / RewardPoolBody) — there is no signature field +// on the body, so common.ProtoRecover marshals it as-is. Used by every +// reward / reward-pool validator and finalizer. +func (s *Server) recoverDeadlinedSigner(currentHeight, deadlineHeight int64, msg proto.Message, signature string) (string, error) { if currentHeight > deadlineHeight { return "", fmt.Errorf("%w: current height %d > deadline %d", ErrRewardExpired, currentHeight, deadlineHeight) } - - // Convert hex data to bytes for signing - dataBytes, err := hex.DecodeString(signatureData) + signer, err := common.ProtoRecover(msg, signature) if err != nil { - return "", fmt.Errorf("%w: invalid hex data: %v", ErrRewardSignatureInvalid, err) + return "", fmt.Errorf("%w: %v", ErrRewardSignatureInvalid, err) } - - // Recover signer from signature - _, signer, err := common.EthRecover(signature, dataBytes) - if err != nil { - return "", fmt.Errorf("%w: failed to recover signer: %v", ErrRewardSignatureInvalid, err) - } - return signer, nil } @@ -172,20 +193,36 @@ func (s *Server) finalizeRewardTransaction(ctx context.Context, req *abcitypes.F return rewardMsg, nil } -func (s *Server) finalizeRewards(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, rewardMsg *corev1.RewardMessage, sender string) error { - if rewardMsg == nil { +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) + } - switch action := rewardMsg.Action.(type) { - case *corev1.RewardMessage_Create: - if err := s.finalizeCreateReward(ctx, req, txhash, messageIndex, action.Create, sender); err != nil { + signer, err := s.recoverDeadlinedSigner(req.Height, envelope.Body.DeadlineBlockHeight, envelope.Body, envelope.Signature) + if err != nil { + return errors.Join(ErrRewardMessageFinalization, fmt.Errorf("signature validation failed: %w", err)) + } + + switch action := envelope.Body.Action.(type) { + case *corev1.RewardBody_Create: + if err := s.finalizeCreateReward(ctx, req, txhash, messageIndex, action.Create, signer); err != nil { return errors.Join(ErrRewardMessageFinalization, err) } return nil - case *corev1.RewardMessage_Delete: - if err := s.finalizeDeleteReward(ctx, req, txhash, messageIndex, action.Delete, sender); err != nil { + case *corev1.RewardBody_Delete: + if err := s.finalizeDeleteReward(ctx, action.Delete, signer); err != nil { return errors.Join(ErrRewardMessageFinalization, err) } return nil @@ -195,45 +232,37 @@ func (s *Server) finalizeRewards(ctx context.Context, req *abcitypes.FinalizeBlo } } -func (s *Server) finalizeCreateReward(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, createReward *corev1.CreateReward, sender string) error { - // Validate signature and get signer - signatureData := common.CreateDeterministicCreateRewardData(createReward) - signer, err := s.validateRewardSignature(req.Height, createReward.Signature, createReward.DeadlineBlockHeight, signatureData) - if err != nil { - return fmt.Errorf("create reward signature validation failed: %w", err) +func (s *Server) finalizeCreateReward(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, createReward *corev1.CreateReward, signer string) error { + // New CreateReward must reference an existing first-class pool. Re-check + // authorization against post-prior-tx state — block ordering can rotate + // the signer out between validate and finalize. + qtx := s.getDb() + if err := s.checkPoolAuthorization(ctx, qtx, createReward.RewardsManagerPubkey, signer); err != nil { + return err } - // Generate deterministic address for the new reward txhashBytes, err := common.HexToBytes(txhash) if err != nil { return fmt.Errorf("invalid txhash: %w", err) } rewardAddress := common.CreateAddress(txhashBytes, s.config.GenesisFile.ChainID, req.Height, messageIndex, "") - // Convert claim authorities to string array - claimAuthorities := make([]string, len(createReward.ClaimAuthorities)) - for i, auth := range createReward.ClaimAuthorities { - claimAuthorities[i] = auth.Address - } - - // Marshal the raw message rawMessage, err := proto.Marshal(createReward) if err != nil { return fmt.Errorf("failed to marshal create reward message: %w", err) } - qtx := s.getDb() if err := qtx.InsertCoreReward(ctx, db.InsertCoreRewardParams{ - TxHash: txhash, - Index: messageIndex, - Address: rewardAddress, - Sender: signer, // Use verified signer instead of passed sender - RewardID: createReward.RewardId, - Name: createReward.Name, - Amount: int64(createReward.Amount), - ClaimAuthorities: claimAuthorities, - RawMessage: rawMessage, - BlockHeight: req.Height, + TxHash: txhash, + Index: messageIndex, + Address: rewardAddress, + Sender: signer, + RewardID: createReward.RewardId, + Name: createReward.Name, + Amount: int64(createReward.Amount), + RewardsManagerPubkey: pgtype.Text{String: createReward.RewardsManagerPubkey, Valid: true}, + RawMessage: rawMessage, + BlockHeight: req.Height, }); err != nil { return fmt.Errorf("failed to insert reward: %w", err) } @@ -241,36 +270,23 @@ func (s *Server) finalizeCreateReward(ctx context.Context, req *abcitypes.Finali return nil } -func (s *Server) finalizeDeleteReward(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, deleteReward *corev1.DeleteReward, sender string) error { - // Validate signature and get signer - signatureData := common.CreateDeterministicDeleteRewardData(deleteReward) - signer, err := s.validateRewardSignature(req.Height, deleteReward.Signature, deleteReward.DeadlineBlockHeight, signatureData) - if err != nil { - return fmt.Errorf("delete reward signature validation failed: %w", err) - } - - // Verify signer is authorized to delete this reward - existingReward, err := s.getDb().GetReward(ctx, deleteReward.Address) +func (s *Server) finalizeDeleteReward(ctx context.Context, deleteReward *corev1.DeleteReward, signer string) error { + // Re-check authorization against post-prior-tx state — a sibling tx in + // the same block can rotate the signer out of the reward's pool between + // validate (pre-block state) and finalize (post-prior-tx state). + qtx := s.getDb() + existingReward, err := qtx.GetReward(ctx, deleteReward.Address) if err != nil { - return fmt.Errorf("failed to get existing reward: %w", err) - } - - // Check if signer is in the claim authorities (case insensitive) - authorized := false - for _, auth := range existingReward.ClaimAuthorities { - if strings.EqualFold(auth, signer) { - authorized = true - break + if errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("%w: reward %s not found", ErrRewardMessageValidation, deleteReward.Address) } + return fmt.Errorf("failed to get reward at finalize: %w", err) } - if !authorized { - return fmt.Errorf("signer %s not authorized to delete reward %s", signer, deleteReward.Address) + if !slices.Contains(existingReward.ClaimAuthorities, strings.ToLower(strings.TrimSpace(signer))) { + return fmt.Errorf("%w: signer %s no longer authorized to delete reward %s", ErrRewardUnauthorized, signer, deleteReward.Address) } - - qtx := s.getDb() if err := qtx.DeleteCoreReward(ctx, deleteReward.Address); err != nil { return fmt.Errorf("failed to delete reward: %w", err) } - return nil } diff --git a/pkg/core/server/rewards_legacy.go b/pkg/core/server/rewards_legacy.go new file mode 100644 index 00000000..a89c8e88 --- /dev/null +++ b/pkg/core/server/rewards_legacy.go @@ -0,0 +1,210 @@ +package server + +import ( + "context" + "errors" + "fmt" + "slices" + "strings" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/OpenAudio/go-openaudio/pkg/common" + "github.com/OpenAudio/go-openaudio/pkg/core/db" + "github.com/OpenAudio/go-openaudio/pkg/rewards" + abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" + "google.golang.org/protobuf/proto" +) + +// Legacy reward wire-compat layer. +// +// Pre-pool-rollout, RewardMessage was a oneof of CreateReward / DeleteReward +// with deadline + signature embedded inside each action. The current +// body+signature envelope is wire-incompatible: legacy bytes have no +// field at tag 1 (the new body) so the new proto unmarshals them with +// Body == nil. +// +// This file handles the case where a legacy-shaped reward tx is encountered +// during block-sync-from-genesis. We re-decode the unknown fields as +// LegacyRewardMessage, recover the signer using the legacy signing scheme +// (sha256 over a pipe-delimited canonical string — see +// pkg/common/legacy_reward_signing.go), and bind the reward to a real-RM +// pool by looking up any one of its inline claim_authorities in the +// launchpad_authority_rm seed (matching the migration backfill exactly). +// For DeleteReward we re-check authorization against the existing reward's +// pool authorities. Rewards whose authorities don't match any launchpad +// entry are inserted with NULL rewards_manager_pubkey. +// +// Live-traffic note: post-rollout, no client should produce legacy bytes — +// the SDK and API repo only emit the new envelope. This path is dormant +// after the migration window and only activates for replay. + +// tryParseLegacyReward inspects a RewardMessage that decoded with Body == nil +// and re-attempts to decode its bytes as the legacy oneof shape. Returns nil +// if no legacy action is present (e.g., the bytes really were empty / from a +// future incompatible version). +func tryParseLegacyReward(rm *corev1.RewardMessage) (*corev1.LegacyRewardMessage, error) { + if rm == nil || rm.Body != nil { + return nil, nil + } + // proto-go preserves unknown fields by default; round-tripping the new + // RewardMessage through Marshal recovers the original tag 1000/1001 bytes. + raw, err := proto.Marshal(rm) + if err != nil { + return nil, fmt.Errorf("legacy reward: re-marshal: %w", err) + } + if len(raw) == 0 { + return nil, nil + } + var legacy corev1.LegacyRewardMessage + if err := proto.Unmarshal(raw, &legacy); err != nil { + return nil, fmt.Errorf("legacy reward: unmarshal: %w", err) + } + if legacy.Action == nil { + return nil, nil + } + return &legacy, nil +} + +// finalizeLegacyRewardTransaction applies a legacy-shape reward tx. For +// CreateReward, the reward's RM is resolved by looking up any one of its +// inline claim_authorities in the launchpad_authority_rm mapping (the +// schema migration populates this table); rewards whose authorities +// don't match any known launchpad authority are inserted with NULL +// rewards_manager_pubkey. For DeleteReward, the row is removed after +// re-checking authorization against the existing reward's pool +// authorities. +func (s *Server) finalizeLegacyRewardTransaction(ctx context.Context, req *abcitypes.FinalizeBlockRequest, legacy *corev1.LegacyRewardMessage, txhash string, messageIndex int64) error { + switch a := legacy.Action.(type) { + case *corev1.LegacyRewardMessage_Create: + signer, err := s.recoverLegacyCreateRewardSigner(req.Height, a.Create) + if err != nil { + return errors.Join(ErrRewardMessageFinalization, err) + } + if err := s.finalizeLegacyCreateReward(ctx, req, txhash, messageIndex, a.Create, signer); err != nil { + return errors.Join(ErrRewardMessageFinalization, err) + } + return nil + case *corev1.LegacyRewardMessage_Delete: + signer, err := s.recoverLegacyDeleteRewardSigner(req.Height, a.Delete) + if err != nil { + return errors.Join(ErrRewardMessageFinalization, err) + } + if err := s.finalizeLegacyDeleteReward(ctx, a.Delete, signer); err != nil { + return errors.Join(ErrRewardMessageFinalization, err) + } + return nil + default: + return fmt.Errorf("tx: %s, message index: %d, unknown legacy reward action", txhash, messageIndex) + } +} + +func (s *Server) recoverLegacyCreateRewardSigner(currentHeight int64, cr *corev1.LegacyCreateReward) (string, error) { + if currentHeight > cr.DeadlineBlockHeight { + return "", fmt.Errorf("%w: current height %d > deadline %d", ErrRewardExpired, currentHeight, cr.DeadlineBlockHeight) + } + signer, err := common.LegacyRecoverCreateReward(cr) + if err != nil { + return "", fmt.Errorf("%w: %v", ErrRewardSignatureInvalid, err) + } + return signer, nil +} + +func (s *Server) recoverLegacyDeleteRewardSigner(currentHeight int64, dr *corev1.LegacyDeleteReward) (string, error) { + if currentHeight > dr.DeadlineBlockHeight { + return "", fmt.Errorf("%w: current height %d > deadline %d", ErrRewardExpired, currentHeight, dr.DeadlineBlockHeight) + } + signer, err := common.LegacyRecoverDeleteReward(dr) + if err != nil { + return "", fmt.Errorf("%w: %v", ErrRewardSignatureInvalid, err) + } + return signer, nil +} + +func (s *Server) finalizeLegacyCreateReward(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, cr *corev1.LegacyCreateReward, signer string) error { + txhashBytes, err := common.HexToBytes(txhash) + if err != nil { + return fmt.Errorf("invalid txhash: %w", err) + } + rewardAddress := common.CreateAddress(txhashBytes, s.config.GenesisFile.ChainID, req.Height, messageIndex, "") + + qtx := s.getDb() + + // Resolve the reward's RM by looking for a launchpad-derived authority + // among the message's inline claim_authorities. The launchpad_authority_rm + // table (populated by the schema migration) maps each per-mint claim + // authority (lowercased eth hex) to the Solana reward manager state + // account that mint's rewards live under. This produces the SAME + // (RM, authorities) state the migration backfill produced for + // pre-migration rows, so historical replay and the migration agree + // on the resulting apphash. + authorityAddrs := make([]string, 0, len(cr.ClaimAuthorities)) + for _, auth := range cr.ClaimAuthorities { + authorityAddrs = append(authorityAddrs, auth.Address) + } + canonicalAuthorities := rewards.CanonicalAuthorities(authorityAddrs) + + var rmPubkey pgtype.Text + if len(canonicalAuthorities) > 0 { + rm, err := qtx.GetLaunchpadRMByAuthority(ctx, canonicalAuthorities) + switch { + case errors.Is(err, pgx.ErrNoRows): + // No matching launchpad authority. Reward stays orphaned (NULL + // rewards_manager_pubkey, no pool). Mirrors what the migration + // does for rows whose claim_authorities don't include any known + // launchpad-derived authority. + case err != nil: + return fmt.Errorf("failed to resolve launchpad RM: %w", err) + default: + // Upsert (UNION) the pool so successive legacy replays of + // rewards under the same RM accumulate into the same authority + // set the migration's UNION-based backfill produces. + if err := qtx.UpsertLegacyReplayRewardPool(ctx, db.UpsertLegacyReplayRewardPoolParams{ + RewardsManagerPubkey: rm, + Authorities: canonicalAuthorities, + }); err != nil { + return fmt.Errorf("failed to upsert reward pool for legacy replay: %w", err) + } + rmPubkey = pgtype.Text{String: rm, Valid: true} + } + } + + rawMessage, err := proto.Marshal(cr) + if err != nil { + return fmt.Errorf("failed to marshal legacy create reward: %w", err) + } + if err := qtx.InsertCoreReward(ctx, db.InsertCoreRewardParams{ + TxHash: txhash, + Index: messageIndex, + Address: rewardAddress, + Sender: signer, + RewardID: cr.RewardId, + Name: cr.Name, + Amount: int64(cr.Amount), + RewardsManagerPubkey: rmPubkey, + RawMessage: rawMessage, + BlockHeight: req.Height, + }); err != nil { + return fmt.Errorf("failed to insert legacy reward: %w", err) + } + return nil +} + +func (s *Server) finalizeLegacyDeleteReward(ctx context.Context, dr *corev1.LegacyDeleteReward, signer string) error { + qtx := s.getDb() + existingReward, err := qtx.GetReward(ctx, dr.Address) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("%w: reward %s not found", ErrRewardMessageValidation, dr.Address) + } + return fmt.Errorf("legacy delete: failed to get reward: %w", err) + } + if !slices.Contains(existingReward.ClaimAuthorities, strings.ToLower(strings.TrimSpace(signer))) { + return fmt.Errorf("%w: legacy signer %s no longer authorized to delete reward %s", ErrRewardUnauthorized, signer, dr.Address) + } + if err := qtx.DeleteCoreReward(ctx, dr.Address); err != nil { + return fmt.Errorf("failed to delete legacy reward: %w", err) + } + return nil +} diff --git a/pkg/core/server/rewards_legacy_test.go b/pkg/core/server/rewards_legacy_test.go new file mode 100644 index 00000000..c08fbb6c --- /dev/null +++ b/pkg/core/server/rewards_legacy_test.go @@ -0,0 +1,226 @@ +package server + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "strings" + "testing" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/OpenAudio/go-openaudio/pkg/common" + "github.com/ethereum/go-ethereum/crypto" + "google.golang.org/protobuf/proto" +) + +// TestTryParseLegacyReward_ProtoUnknownFieldRoundtrip exercises the heart of +// the wire-compat layer: when a legacy CreateReward (encoded against the old +// RewardMessage shape) is unmarshaled by the new RewardMessage proto, the +// new proto's Body is nil, but the original tag-1000 bytes survive as +// preserved unknown fields. tryParseLegacyReward re-marshals and re-decodes +// those unknown bytes as LegacyRewardMessage and recovers the action. +func TestTryParseLegacyReward_ProtoUnknownFieldRoundtrip(t *testing.T) { + priv, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("genkey: %v", err) + } + signer := crypto.PubkeyToAddress(priv.PublicKey).Hex() + + t.Run("LegacyCreateReward", func(t *testing.T) { + legacyCreate := &corev1.LegacyCreateReward{ + RewardId: "legacy-r-1", + Name: "Legacy 1", + Amount: 42, + ClaimAuthorities: []*corev1.ClaimAuthority{{Address: signer, Name: "Owner"}}, + DeadlineBlockHeight: 999_999, + } + // Sign with the legacy scheme and stash the signature back on the proto. + sig := signLegacyCreate(t, priv, legacyCreate) + legacyCreate.Signature = sig + + legacyMsg := &corev1.LegacyRewardMessage{ + Action: &corev1.LegacyRewardMessage_Create{Create: legacyCreate}, + } + // Marshal as legacy bytes — what an old node would have written to chain. + legacyBytes, err := proto.Marshal(legacyMsg) + if err != nil { + t.Fatalf("marshal legacy: %v", err) + } + + // Unmarshal those bytes into the *new* RewardMessage shape. Tag 1000 is + // unknown to the new shape; proto-go preserves it as unknown fields. + var newMsg corev1.RewardMessage + if err := proto.Unmarshal(legacyBytes, &newMsg); err != nil { + t.Fatalf("unmarshal new shape: %v", err) + } + if newMsg.Body != nil { + t.Fatalf("expected new shape to decode with Body == nil") + } + + // Wire-compat layer should recover the legacy action. + recovered, err := tryParseLegacyReward(&newMsg) + if err != nil { + t.Fatalf("tryParseLegacyReward: %v", err) + } + if recovered == nil { + t.Fatalf("expected legacy action to be recovered") + } + create := recovered.GetCreate() + if create == nil { + t.Fatalf("expected create action; got %T", recovered.Action) + } + if create.RewardId != "legacy-r-1" || create.Amount != 42 { + t.Fatalf("recovered fields wrong: %+v", create) + } + + // Signer recovery against the legacy signing scheme must yield the + // original key. + recoveredSigner, err := common.LegacyRecoverCreateReward(create) + if err != nil { + t.Fatalf("recover signer: %v", err) + } + if !strings.EqualFold(recoveredSigner, signer) { + t.Fatalf("recovered signer %q want %q", recoveredSigner, signer) + } + }) + + t.Run("LegacyDeleteReward", func(t *testing.T) { + legacyDelete := &corev1.LegacyDeleteReward{ + Address: "0xreward", + DeadlineBlockHeight: 100, + } + sig := signLegacyDelete(t, priv, legacyDelete) + legacyDelete.Signature = sig + + legacyMsg := &corev1.LegacyRewardMessage{ + Action: &corev1.LegacyRewardMessage_Delete{Delete: legacyDelete}, + } + legacyBytes, err := proto.Marshal(legacyMsg) + if err != nil { + t.Fatalf("marshal: %v", err) + } + var newMsg corev1.RewardMessage + if err := proto.Unmarshal(legacyBytes, &newMsg); err != nil { + t.Fatalf("unmarshal: %v", err) + } + recovered, err := tryParseLegacyReward(&newMsg) + if err != nil { + t.Fatalf("tryParseLegacyReward: %v", err) + } + if recovered == nil || recovered.GetDelete() == nil { + t.Fatalf("expected delete action recovered") + } + got, err := common.LegacyRecoverDeleteReward(recovered.GetDelete()) + if err != nil { + t.Fatalf("recover: %v", err) + } + if !strings.EqualFold(got, signer) { + t.Fatalf("recovered %q want %q", got, signer) + } + }) + + t.Run("NewShapeIsLeftAlone", func(t *testing.T) { + // A genuine new-shape RewardMessage (Body populated) must NOT trip the + // legacy path; tryParseLegacyReward returns (nil, nil) and the + // caller's existing dispatch handles it. + newMsg := &corev1.RewardMessage{ + Body: &corev1.RewardBody{ + DeadlineBlockHeight: 1, + Action: &corev1.RewardBody_Delete{Delete: &corev1.DeleteReward{Address: "0xnew"}}, + }, + Signature: "deadbeef", + } + recovered, err := tryParseLegacyReward(newMsg) + if err != nil { + t.Fatalf("err on new shape: %v", err) + } + if recovered != nil { + t.Fatalf("legacy path triggered on new-shape message") + } + }) + + t.Run("EmptyMessageIsNotLegacy", func(t *testing.T) { + var empty corev1.RewardMessage + recovered, err := tryParseLegacyReward(&empty) + if err != nil { + t.Fatalf("err: %v", err) + } + if recovered != nil { + t.Fatalf("expected nil for empty message; got %+v", recovered) + } + }) +} + +// TestIsValidRewardTransaction_RejectsLegacyAtValidate confirms the +// asymmetric gate: live-validation paths (CheckTx, ProcessProposal) refuse +// legacy-format reward txs even though FinalizeBlock still applies them +// during historical replay. This closes the loophole where an attacker +// could craft legacy bytes (with arbitrary inline claim_authorities) and +// bypass the new pool gate, since legacy CreateReward had no +// signer-membership check. +func TestIsValidRewardTransaction_RejectsLegacyAtValidate(t *testing.T) { + priv, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("genkey: %v", err) + } + cr := &corev1.LegacyCreateReward{ + RewardId: "live-legacy", + Name: "should be rejected", + Amount: 1, + ClaimAuthorities: []*corev1.ClaimAuthority{{Address: crypto.PubkeyToAddress(priv.PublicKey).Hex(), Name: "x"}}, + DeadlineBlockHeight: 999_999, + } + cr.Signature = signLegacyCreate(t, priv, cr) + legacyMsg := &corev1.LegacyRewardMessage{Action: &corev1.LegacyRewardMessage_Create{Create: cr}} + legacyBytes, err := proto.Marshal(legacyMsg) + if err != nil { + t.Fatalf("marshal legacy: %v", err) + } + var newMsg corev1.RewardMessage + if err := proto.Unmarshal(legacyBytes, &newMsg); err != nil { + t.Fatalf("unmarshal new shape: %v", err) + } + + signedTx := &corev1.SignedTransaction{ + Transaction: &corev1.SignedTransaction_Reward{Reward: &newMsg}, + } + + // Validate against an empty Server — we only need the dispatch to run far + // enough to confirm legacy bytes are rejected before any DB access. + s := &Server{} + err = s.isValidRewardTransaction(context.Background(), signedTx, 1) + if err == nil { + t.Fatalf("expected legacy reward tx to be rejected at validate-time") + } + if !strings.Contains(err.Error(), "legacy reward wire format is not accepted") { + t.Fatalf("expected legacy-rejection error, got: %v", err) + } +} + +func signLegacyCreate(t *testing.T, priv *ecdsa.PrivateKey, cr *corev1.LegacyCreateReward) string { + t.Helper() + data := common.LegacyDeterministicCreateRewardData(cr) + bytes, err := hex.DecodeString(data) + if err != nil { + t.Fatalf("decode hex: %v", err) + } + sig, err := common.EthSign(priv, bytes) + if err != nil { + t.Fatalf("sign: %v", err) + } + return sig +} + +func signLegacyDelete(t *testing.T, priv *ecdsa.PrivateKey, dr *corev1.LegacyDeleteReward) string { + t.Helper() + data := common.LegacyDeterministicDeleteRewardData(dr) + bytes, err := hex.DecodeString(data) + if err != nil { + t.Fatalf("decode hex: %v", err) + } + sig, err := common.EthSign(priv, bytes) + if err != nil { + t.Fatalf("sign: %v", err) + } + return sig +} diff --git a/pkg/core/server/state_sync.go b/pkg/core/server/state_sync.go index 60624ad6..7af4041c 100644 --- a/pkg/core/server/state_sync.go +++ b/pkg/core/server/state_sync.go @@ -381,6 +381,8 @@ func (s *Server) createPgDump(logger *zap.Logger, latestSnapshotDir string) erro "core_parties", "core_deals", "core_rewards", + "core_reward_pools", + "launchpad_authority_rm", "core_uploads", "validator_history", } diff --git a/pkg/integration_tests/12_rewards_test.go b/pkg/integration_tests/12_rewards_test.go index cf93aca5..142ab6fe 100644 --- a/pkg/integration_tests/12_rewards_test.go +++ b/pkg/integration_tests/12_rewards_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "connectrpc.com/connect" v1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" "github.com/OpenAudio/go-openaudio/pkg/common" "github.com/OpenAudio/go-openaudio/pkg/integration_tests/utils" @@ -41,33 +42,44 @@ func TestRewardsLifecycle(t *testing.T) { t.Logf("creator key: %s", creatorAddr) t.Logf("deleter key: %s", deleterAddr) - // Step 1: Create two rewards with different claim authorities - // Reward 1: only creator as claim authority + // Under the pool-based model authorities live on the pool, not on + // the reward. To preserve the per-reward authority granularity of + // the original test (reward1: creator only; reward2: creator + + // deleter), we create two pools. + rmPubkeyA, rmPrivA := freshRewardManager(t) + rmPubkeyB, rmPrivB := freshRewardManager(t) + if _, err := creator.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkeyA, + Authorities: []string{creatorAddr}, + }, rmPrivA, 999999); err != nil { + t.Fatalf("Failed to create pool A: %v", err) + } + if _, err := creator.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkeyB, + Authorities: []string{creatorAddr, deleterAddr}, + }, rmPrivB, 999999); err != nil { + t.Fatalf("Failed to create pool B: %v", err) + } + + // Step 1: Create two rewards in different pools. reward1, err := creator.Rewards.CreateReward(ctx, &v1.CreateReward{ - RewardId: "reward1", - Name: "Test Reward 1", - Amount: 1000, - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: creatorAddr, Name: "Creator"}, - }, - DeadlineBlockHeight: 999999, - }) + RewardId: "reward1", + Name: "Test Reward 1", + Amount: 1000, + RewardsManagerPubkey: rmPubkeyA, + }, 999999) if err != nil { t.Fatalf("Failed to create reward1: %v", err) } t.Logf("Created reward1 at address: %s", reward1.Address) - // Reward 2: creator and deleter as claim authorities + // Reward 2: creator and deleter as claim authorities (via pool B). reward2, err := creator.Rewards.CreateReward(ctx, &v1.CreateReward{ - RewardId: "reward2", - Name: "Test Reward 2", - Amount: 2000, - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: creatorAddr, Name: "Creator"}, - {Address: deleterAddr, Name: "Deleter"}, - }, - DeadlineBlockHeight: 999999, - }) + RewardId: "reward2", + Name: "Test Reward 2", + Amount: 2000, + RewardsManagerPubkey: rmPubkeyB, + }, 999999) if err != nil { t.Fatalf("Failed to create reward2: %v", err) } @@ -100,8 +112,7 @@ func TestRewardsLifecycle(t *testing.T) { // Step 3: Deleter deletes reward2 deleteHash, err := deleter.Rewards.DeleteReward(ctx, &v1.DeleteReward{ Address: reward2.Address, - DeadlineBlockHeight: 999999, - }) + }, 999999) if err != nil { t.Fatalf("Failed to delete reward2: %v", err) } @@ -135,7 +146,6 @@ func TestRewardsLifecycle(t *testing.T) { }) t.Run("Test Reward Attestations with Claim Authorities", func(t *testing.T) { - t.Skip("artist-coin attestations are temporarily disabled; see GetRewardAttestation in pkg/core/server/connect.go") // Generate random private keys for claim authorities authority1Key, err := crypto.GenerateKey() @@ -166,17 +176,20 @@ func TestRewardsLifecycle(t *testing.T) { t.Logf("authority2 address: %s", authority2Addr) t.Logf("unauthorized address: %s", unauthorizedAddr) - // Create a reward with authority1 and authority2 as claim authorities + // Create a pool with both authorities and a reward in it. + rmPubkey, rmPriv := freshRewardManager(t) + if _, err := authority1.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{authority1Addr, authority2Addr}, + }, rmPriv, 999999); err != nil { + t.Fatalf("Failed to create pool: %v", err) + } reward, err := authority1.Rewards.CreateReward(ctx, &v1.CreateReward{ - RewardId: "attestation_test_reward", - Name: "Attestation Test Reward", - Amount: 5000, - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: authority1Addr, Name: "Authority 1"}, - {Address: authority2Addr, Name: "Authority 2"}, - }, - DeadlineBlockHeight: 999999, - }) + RewardId: "attestation_test_reward", + Name: "Attestation Test Reward", + Amount: 5000, + RewardsManagerPubkey: rmPubkey, + }, 999999) if err != nil { t.Fatalf("Failed to create reward: %v", err) } @@ -216,10 +229,13 @@ func TestRewardsLifecycle(t *testing.T) { } t.Logf("authority2 successfully got attestation: %s", attestation2.Attestation) - // Test 3: unauthorized user should NOT be able to get attestation + // Test 3: unauthorized user should NOT be able to get attestation. + // Amount must match the reward amount (5000) so that the call + // passes Validate and actually exercises the auth gate; otherwise + // it would fail at amount validation instead. _, err = unauthorized.Rewards.GetRewardAttestation(ctx, &v1.GetRewardAttestationRequest{ EthRecipientAddress: recipientAddr, - Amount: 1000, + Amount: 5000, RewardAddress: reward.Address, RewardId: "attestation_test_reward", Specifier: specifier, @@ -229,28 +245,37 @@ func TestRewardsLifecycle(t *testing.T) { if err == nil { t.Fatalf("unauthorized user should NOT be able to get attestation, but it succeeded") } + if got := connect.CodeOf(err); got != connect.CodePermissionDenied { + t.Fatalf("expected PermissionDenied for unauthorized user, got %v: %v", got, err) + } t.Logf("unauthorized user correctly failed to get attestation: %v", err) // Test 4: Verify authority1 cannot get attestation for a reward they're not authorized for - // Create another reward with only authority2 + // Create another pool with only authority2 + a reward in it. + rmPubkey2, rmPriv2 := freshRewardManager(t) + if _, err := authority2.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkey2, + Authorities: []string{authority2Addr}, + }, rmPriv2, 999999); err != nil { + t.Fatalf("Failed to create pool 2: %v", err) + } reward2, err := authority2.Rewards.CreateReward(ctx, &v1.CreateReward{ - RewardId: "attestation_test_reward_2", - Name: "Attestation Test Reward 2", - Amount: 3000, - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: authority2Addr, Name: "Authority 2"}, - }, - DeadlineBlockHeight: 999999, - }) + RewardId: "attestation_test_reward_2", + Name: "Attestation Test Reward 2", + Amount: 3000, + RewardsManagerPubkey: rmPubkey2, + }, 999999) if err != nil { t.Fatalf("Failed to create reward2: %v", err) } t.Logf("Created reward2 at address: %s", reward2.Address) - // authority1 should NOT be able to get attestation for reward2 + // authority1 should NOT be able to get attestation for reward2. + // Amount matches reward2 (3000) so the call reaches the auth gate + // rather than failing earlier at amount validation. _, err = authority1.Rewards.GetRewardAttestation(ctx, &v1.GetRewardAttestationRequest{ EthRecipientAddress: recipientAddr, - Amount: 500, + Amount: 3000, RewardAddress: reward2.Address, RewardId: "attestation_test_reward_2", Specifier: specifier, @@ -260,13 +285,15 @@ func TestRewardsLifecycle(t *testing.T) { if err == nil { t.Fatalf("authority1 should NOT be able to get attestation for reward2, but it succeeded") } + if got := connect.CodeOf(err); got != connect.CodePermissionDenied { + t.Fatalf("expected PermissionDenied for cross-pool authority, got %v: %v", got, err) + } t.Logf("authority1 correctly failed to get attestation for reward2: %v", err) t.Logf("All reward attestation tests passed successfully!") }) t.Run("Test with Amount Validation", func(t *testing.T) { - t.Skip("artist-coin attestations are temporarily disabled; see GetRewardAttestation in pkg/core/server/connect.go") // Generate a new claim authority key authorityKey, err := crypto.GenerateKey() @@ -277,7 +304,9 @@ func TestRewardsLifecycle(t *testing.T) { authority := sdk.NewOpenAudioSDK(nodeUrl) authority.SetPrivKey(authorityKey) - // Generate a key for creating the reward + // Generate a key for creating the reward. Note this isn't the pool + // authority; the pool is created via authority's SDK so the envelope + // signer is in the initial authority list. creatorKey, err := crypto.GenerateKey() if err != nil { t.Fatalf("Failed to generate creator key: %v", err) @@ -285,16 +314,26 @@ func TestRewardsLifecycle(t *testing.T) { creator := sdk.NewOpenAudioSDK(nodeUrl) creator.SetPrivKey(creatorKey) - // Create a reward with specific amount - reward, err := creator.Rewards.CreateReward(ctx, &v1.CreateReward{ - RewardId: "amount_test", - Name: "Amount Test Reward", - Amount: 100, // Fixed amount - ClaimAuthorities: []*v1.ClaimAuthority{ - {Address: authorityAddr, Name: "Test Authority"}, - }, - DeadlineBlockHeight: 999999, - }) + // Create a pool that names authority + a reward bound to it. + // Authority signs the envelope (it's the lone initial authority); + // the RM keypair signs the inner rm_owner_signature. + rmPubkey, rmPriv := freshRewardManager(t) + if _, err := authority.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{authorityAddr}, + }, rmPriv, 999999); err != nil { + t.Fatalf("Failed to create pool: %v", err) + } + // authority is the only pool member, so it must create the reward + // (validateCreateReward gates on signer ∈ pool.authorities). The + // `creator` SDK above is unused under the pool model. + _ = creator + reward, err := authority.Rewards.CreateReward(ctx, &v1.CreateReward{ + RewardId: "amount_test", + Name: "Amount Test Reward", + Amount: 100, + RewardsManagerPubkey: rmPubkey, + }, 999999) if err != nil { t.Fatalf("Failed to create reward: %v", err) } diff --git a/pkg/integration_tests/13_reward_pools_test.go b/pkg/integration_tests/13_reward_pools_test.go new file mode 100644 index 00000000..5cc93b43 --- /dev/null +++ b/pkg/integration_tests/13_reward_pools_test.go @@ -0,0 +1,324 @@ +package integration_tests + +import ( + "context" + "crypto/ed25519" + "crypto/rand" + "strings" + "testing" + + v1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/OpenAudio/go-openaudio/pkg/common" + "github.com/OpenAudio/go-openaudio/pkg/integration_tests/utils" + "github.com/OpenAudio/go-openaudio/pkg/sdk" + "github.com/ethereum/go-ethereum/crypto" + "github.com/mr-tron/base58/base58" +) + +// freshRewardManager returns a random ed25519 keypair representing a +// freshly-minted Solana reward manager state account. The pubkey (base58- +// encoded) doubles as the cometbft rewards_manager_pubkey; the private +// key is what the SDK uses to sign the envelope's rm_owner_signature, +// which proves to the validator that the caller controls the RM keypair +// (defeating frontrunning of pool creation). +func freshRewardManager(t *testing.T) (string, ed25519.PrivateKey) { + t.Helper() + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("ed25519.GenerateKey: %v", err) + } + return base58.Encode(pub), priv +} + +// freshSolanaPubkey is a convenience wrapper for tests that only need a +// well-formed Solana pubkey shape and don't need to sign with the keypair +// (e.g., negative tests that exercise rejection on the pubkey itself). +func freshSolanaPubkey(t *testing.T) string { + t.Helper() + pub, _ := freshRewardManager(t) + return pub +} + +// TestRewardPoolsLifecycle exercises the cometbft RewardPool transactions: +// pool creation, gating CreateReward on pool membership, rotating +// authorities via SetRewardPoolAuthorities, and verifying that a rotated-out +// signer is no longer accepted. +func TestRewardPoolsLifecycle(t *testing.T) { + ctx := context.Background() + nodeUrl := utils.DiscoveryOneRPC + + if err := utils.WaitForDevnetHealthy(); err != nil { + t.Fatalf("Devnet not ready: %v", err) + } + + // Each test run gets its own fresh RM keypair so reruns don't collide on + // the pool's identity (uniqueness is enforced server-side). The private + // key is what the SDK uses to produce the envelope's + // rm_owner_signature on CreateRewardPool. + rmPubkey, rmPriv := freshRewardManager(t) + otherRmPubkey, otherRmPriv := freshRewardManager(t) + + aliceKey, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("alice key: %v", err) + } + aliceAddr := common.PrivKeyToAddress(aliceKey) + alice := sdk.NewOpenAudioSDK(nodeUrl) + alice.SetPrivKey(aliceKey) + + bobKey, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("bob key: %v", err) + } + bobAddr := common.PrivKeyToAddress(bobKey) + bob := sdk.NewOpenAudioSDK(nodeUrl) + bob.SetPrivKey(bobKey) + + malloryKey, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("mallory key: %v", err) + } + mallory := sdk.NewOpenAudioSDK(nodeUrl) + mallory.SetPrivKey(malloryKey) + + t.Run("CreateRewardPool rejects non-pubkey rewards_manager_pubkey", func(t *testing.T) { + // Pubkey shape check fires before signature verification, so the + // rmPriv we pass here is signing for a different RM than the + // message claims — but the message's pubkey is rejected at the + // shape check before we get there. + _, err := alice.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: "not-a-real-pubkey", + Authorities: []string{aliceAddr}, + }, rmPriv, 999999) + if err == nil { + t.Fatalf("expected non-pubkey rewards_manager_pubkey to be rejected") + } + }) + + t.Run("CreateRewardPool rejects signature signed by wrong RM keypair", func(t *testing.T) { + // Signature is well-formed and ed25519-valid — but produced by + // rmPriv against a body claiming otherRmPubkey. Verification key + // for the validator is otherRmPubkey, which doesn't match the + // rmPriv signing key, so verify fails. + _, err := alice.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: otherRmPubkey, + Authorities: []string{aliceAddr}, + }, rmPriv, 999999) + if err == nil { + t.Fatalf("expected mismatched-keypair signature to be rejected") + } + }) + + t.Run("CreateRewardPool requires signer in initial authorities", func(t *testing.T) { + // Mallory submits a perfectly-signed CreateRewardPool (valid RM + // keypair) but isn't in the initial authorities. The RM-keypair + // gate passes; the signer-membership gate fails. + _, err := mallory.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: otherRmPubkey, + Authorities: []string{aliceAddr, bobAddr}, + }, otherRmPriv, 999999) + if err == nil { + t.Fatalf("expected CreateRewardPool to reject signer not in initial authorities") + } + if _, err := alice.Rewards.GetRewardPool(ctx, otherRmPubkey); err == nil { + t.Fatalf("rejected pool should not have been persisted") + } + }) + + t.Run("Alice creates the pool with [alice, bob]", func(t *testing.T) { + _, err := alice.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{aliceAddr, bobAddr}, + }, rmPriv, 999999) + if err != nil { + t.Fatalf("create pool: %v", err) + } + + pool, err := alice.Rewards.GetRewardPool(ctx, rmPubkey) + if err != nil { + t.Fatalf("get pool: %v", err) + } + if pool.RewardsManagerPubkey != rmPubkey { + t.Fatalf("rewards_manager_pubkey: got %q want %q", pool.RewardsManagerPubkey, rmPubkey) + } + if !containsFold(pool.Authorities, aliceAddr) || !containsFold(pool.Authorities, bobAddr) { + t.Fatalf("authorities missing alice/bob: %v", pool.Authorities) + } + }) + + t.Run("CreateRewardPool with duplicate rewards_manager_pubkey is rejected", func(t *testing.T) { + _, err := alice.Rewards.CreateRewardPool(ctx, &v1.CreateRewardPool{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{aliceAddr}, + }, rmPriv, 999999) + if err == nil { + t.Fatalf("expected duplicate rewards_manager_pubkey to be rejected") + } + }) + + t.Run("Pool member can create a reward in the pool", func(t *testing.T) { + reward, err := bob.Rewards.CreateReward(ctx, &v1.CreateReward{ + RewardId: "pool-reward-1", + Name: "Pool Reward", + Amount: 500, + RewardsManagerPubkey: rmPubkey, + }, 999999) + if err != nil { + t.Fatalf("bob create reward: %v", err) + } + if reward.Address == "" { + t.Fatalf("expected reward address") + } + + // GetRewards must accept caller's checksum-case address (which is what + // common.PrivKeyToAddress returns) even though stored authorities are + // lowercased by CanonicalAuthorities. Guards against the case- + // sensitivity regression. + listed, err := bob.Rewards.GetRewards(ctx, bobAddr) + if err != nil { + t.Fatalf("get rewards by checksum-case bob: %v", err) + } + found := false + for _, r := range listed.Rewards { + if r.Address == reward.Address { + found = true + break + } + } + if !found { + t.Fatalf("expected pool-attached reward %s in GetRewards(%s); got %d rewards", reward.Address, bobAddr, len(listed.Rewards)) + } + }) + + t.Run("Non-member cannot create a reward in the pool", func(t *testing.T) { + _, err := mallory.Rewards.CreateReward(ctx, &v1.CreateReward{ + RewardId: "pool-reward-evil", + Name: "Evil Reward", + Amount: 9999, + RewardsManagerPubkey: rmPubkey, + }, 999999) + if err == nil { + t.Fatalf("expected non-member CreateReward to be rejected") + } + }) + + t.Run("CreateReward without rewards_manager_pubkey is rejected", func(t *testing.T) { + _, err := alice.Rewards.CreateReward(ctx, &v1.CreateReward{ + RewardId: "no-rm", + Name: "no rm", + Amount: 1, + }, 999999) + if err == nil { + t.Fatalf("expected CreateReward without rewards_manager_pubkey to be rejected") + } + }) + + t.Run("SetRewardPoolAuthorities cannot be called by non-member", func(t *testing.T) { + _, err := mallory.Rewards.SetRewardPoolAuthorities(ctx, &v1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{aliceAddr}, + }, 999999) + if err == nil { + t.Fatalf("expected non-member SetRewardPoolAuthorities to be rejected") + } + }) + + t.Run("SetRewardPoolAuthorities cannot set empty list", func(t *testing.T) { + _, err := alice.Rewards.SetRewardPoolAuthorities(ctx, &v1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{}, + }, 999999) + if err == nil { + t.Fatalf("expected empty authorities to be rejected") + } + }) + + t.Run("SetRewardPoolAuthorities rejects non-eth-address entries", func(t *testing.T) { + _, err := alice.Rewards.SetRewardPoolAuthorities(ctx, &v1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{"not-an-address"}, + }, 999999) + if err == nil { + t.Fatalf("expected non-eth-address authority to be rejected (would orphan pool)") + } + }) + + t.Run("Rotate: alice removes bob, leaving only alice", func(t *testing.T) { + _, err := alice.Rewards.SetRewardPoolAuthorities(ctx, &v1.SetRewardPoolAuthorities{ + RewardsManagerPubkey: rmPubkey, + Authorities: []string{aliceAddr}, + }, 999999) + if err != nil { + t.Fatalf("rotate: %v", err) + } + pool, err := alice.Rewards.GetRewardPool(ctx, rmPubkey) + if err != nil { + t.Fatalf("get pool after rotate: %v", err) + } + if len(pool.Authorities) != 1 || !strings.EqualFold(pool.Authorities[0], aliceAddr) { + t.Fatalf("after rotate expected only alice; got %v", pool.Authorities) + } + }) + + t.Run("Bob can no longer create a reward in the pool after rotation", func(t *testing.T) { + _, err := bob.Rewards.CreateReward(ctx, &v1.CreateReward{ + RewardId: "pool-reward-stale", + Name: "Stale Reward", + Amount: 1, + RewardsManagerPubkey: rmPubkey, + }, 999999) + if err == nil { + t.Fatalf("expected rotated-out bob to be rejected") + } + }) + + // === Sender attestation flow === + // + // Pool at rmPubkey now has authorities = [alice]; bob has been rotated out. + // Validator should: sign create for alice (current authority), refuse + // create for bob (not in pool), sign delete for bob (rotated out → ok to + // remove from Solana), refuse delete for alice (still authorized). + + t.Run("CreateSender attestation: pool member alice is signed", func(t *testing.T) { + resp, err := alice.Rewards.GetRewardSenderAttestation(ctx, aliceAddr, rmPubkey) + if err != nil { + t.Fatalf("expected create-sender attestation for current authority alice: %v", err) + } + if resp.Attestation == "" { + t.Fatalf("expected attestation string") + } + }) + + t.Run("CreateSender attestation: rotated-out bob is rejected", func(t *testing.T) { + _, err := alice.Rewards.GetRewardSenderAttestation(ctx, bobAddr, rmPubkey) + if err == nil { + t.Fatalf("expected create-sender attestation to be refused for rotated-out bob") + } + }) + + t.Run("DeleteSender attestation: rotated-out bob is signed", func(t *testing.T) { + resp, err := alice.Rewards.GetDeleteRewardSenderAttestation(ctx, bobAddr, rmPubkey) + if err != nil { + t.Fatalf("expected delete-sender attestation for rotated-out bob: %v", err) + } + if resp.Attestation == "" { + t.Fatalf("expected attestation string") + } + }) + + t.Run("DeleteSender attestation: current authority alice is rejected", func(t *testing.T) { + _, err := alice.Rewards.GetDeleteRewardSenderAttestation(ctx, aliceAddr, rmPubkey) + if err == nil { + t.Fatalf("expected delete-sender attestation to be refused for current authority alice") + } + }) +} + +func containsFold(haystack []string, needle string) bool { + for _, h := range haystack { + if strings.EqualFold(h, needle) { + return true + } + } + return false +} diff --git a/pkg/rewards/reward_pool.go b/pkg/rewards/reward_pool.go new file mode 100644 index 00000000..5d72e8f5 --- /dev/null +++ b/pkg/rewards/reward_pool.go @@ -0,0 +1,29 @@ +package rewards + +import ( + "sort" + "strings" +) + +// CanonicalAuthorities normalizes a list of eth addresses to the canonical +// form stored in core_reward_pools.authorities: lowercased, trimmed, +// deduplicated, non-empty entries, sorted ascending. Producers of pool +// authority sets canonicalize before write so containment checks against +// the gin index on authorities stay deterministic. +func CanonicalAuthorities(authorities []string) []string { + seen := make(map[string]struct{}, len(authorities)) + out := make([]string, 0, len(authorities)) + for _, a := range authorities { + a = strings.ToLower(strings.TrimSpace(a)) + if a == "" { + continue + } + if _, ok := seen[a]; ok { + continue + } + seen[a] = struct{}{} + out = append(out, a) + } + sort.Strings(out) + return out +} diff --git a/pkg/rewards/reward_pool_test.go b/pkg/rewards/reward_pool_test.go new file mode 100644 index 00000000..e3c321ff --- /dev/null +++ b/pkg/rewards/reward_pool_test.go @@ -0,0 +1,75 @@ +package rewards + +import ( + "testing" +) + +func TestCanonicalAuthorities(t *testing.T) { + tests := []struct { + name string + in []string + want []string + }{ + { + name: "empty input", + in: []string{}, + want: []string{}, + }, + { + name: "nil input", + in: nil, + want: []string{}, + }, + { + name: "drops empty and whitespace-only entries", + in: []string{"", "0xabc", " ", "\t", "0xdef"}, + want: []string{"0xabc", "0xdef"}, + }, + { + name: "lowercases mixed case", + in: []string{"0xABC", "0xDeF"}, + want: []string{"0xabc", "0xdef"}, + }, + { + name: "trims surrounding whitespace", + in: []string{" 0xabc ", "\t0xdef\n"}, + want: []string{"0xabc", "0xdef"}, + }, + { + name: "deduplicates after canonicalization", + in: []string{"0xABC", "0xabc", " 0xABC ", "0xdef"}, + want: []string{"0xabc", "0xdef"}, + }, + { + name: "sorts ascending", + in: []string{"0xdef", "0xabc", "0x123"}, + want: []string{"0x123", "0xabc", "0xdef"}, + }, + { + name: "all whitespace results in empty", + in: []string{" ", "\t", ""}, + want: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CanonicalAuthorities(tt.in) + if !equalStringSlices(got, tt.want) { + t.Fatalf("got %v, want %v", got, tt.want) + } + }) + } +} + +func equalStringSlices(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} diff --git a/pkg/sdk/rewards/rewards.go b/pkg/sdk/rewards/rewards.go index c1fee593..ad9494da 100644 --- a/pkg/sdk/rewards/rewards.go +++ b/pkg/sdk/rewards/rewards.go @@ -3,7 +3,9 @@ package rewards import ( "context" "crypto/ecdsa" + "crypto/ed25519" "errors" + "fmt" "connectrpc.com/connect" v1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" @@ -27,65 +29,79 @@ func (r *Rewards) SetPrivKey(privKey *ecdsa.PrivateKey) { r.privKey = privKey } -func (r *Rewards) CreateReward(ctx context.Context, cr *v1.CreateReward) (*v1.GetRewardResponse, error) { - sig, err := common.SignCreateReward(r.privKey, cr) +// signAndSendReward signs the body, wraps it in the RewardMessage envelope +// alongside the signature, and submits the resulting SignedTransaction. +func (r *Rewards) signAndSendReward(ctx context.Context, body *v1.RewardBody) (string, error) { + sig, err := common.ProtoSign(r.privKey, body) if err != nil { - return nil, err + return "", err } - cr.Signature = sig - tx := &v1.SendTransactionRequest{ Transaction: &v1.SignedTransaction{ Transaction: &v1.SignedTransaction_Reward{ - Reward: &v1.RewardMessage{ - Action: &v1.RewardMessage_Create{Create: cr}, - }, + Reward: &v1.RewardMessage{Body: body, Signature: sig}, }, }, } - - req := connect.NewRequest(tx) - resp, err := r.core.SendTransaction(ctx, req) + resp, err := r.core.SendTransaction(ctx, connect.NewRequest(tx)) if err != nil { - return nil, err - } - - txhash := resp.Msg.Transaction.Hash - reward, err := r.core.GetReward(ctx, connect.NewRequest(&v1.GetRewardRequest{ - Txhash: txhash, - })) - if err != nil { - return nil, err + return "", err } - - return reward.Msg, nil + return resp.Msg.GetTransaction().GetHash(), nil } -func (r *Rewards) DeleteReward(ctx context.Context, dr *v1.DeleteReward) (string, error) { - sig, err := common.SignDeleteReward(r.privKey, dr) +// signAndSendRewardPool signs the body with the envelope signer's +// secp256k1 key and submits. rmOwnerSig, when non-nil, is placed in the +// envelope's rm_owner_signature field. It is required when the body's +// action is CreateRewardPool (the validator rejects unsigned creates as +// frontrunning) and ignored otherwise. Callers produce the ed25519 +// signature over common.ProtoSignableBytes(body) using the RM keypair — +// see CreateRewardPool below for the canonical helper. +func (r *Rewards) signAndSendRewardPool(ctx context.Context, body *v1.RewardPoolBody, rmOwnerSig []byte) (string, error) { + sig, err := common.ProtoSign(r.privKey, body) if err != nil { return "", err } - dr.Signature = sig - tx := &v1.SendTransactionRequest{ Transaction: &v1.SignedTransaction{ - Transaction: &v1.SignedTransaction_Reward{ - Reward: &v1.RewardMessage{ - Action: &v1.RewardMessage_Delete{Delete: dr}, + Transaction: &v1.SignedTransaction_RewardPool{ + RewardPool: &v1.RewardPoolMessage{ + Body: body, + Signature: sig, + RmOwnerSignature: rmOwnerSig, }, }, }, } - - req := connect.NewRequest(tx) - deleteRes, err := r.core.SendTransaction(ctx, req) + resp, err := r.core.SendTransaction(ctx, connect.NewRequest(tx)) if err != nil { return "", err } + return resp.Msg.GetTransaction().GetHash(), nil +} - txhash := deleteRes.Msg.GetTransaction().GetHash() - return txhash, nil +func (r *Rewards) CreateReward(ctx context.Context, cr *v1.CreateReward, deadlineBlockHeight int64) (*v1.GetRewardResponse, error) { + body := &v1.RewardBody{ + DeadlineBlockHeight: deadlineBlockHeight, + Action: &v1.RewardBody_Create{Create: cr}, + } + txhash, err := r.signAndSendReward(ctx, body) + if err != nil { + return nil, err + } + reward, err := r.core.GetReward(ctx, connect.NewRequest(&v1.GetRewardRequest{Txhash: txhash})) + if err != nil { + return nil, err + } + return reward.Msg, nil +} + +func (r *Rewards) DeleteReward(ctx context.Context, dr *v1.DeleteReward, deadlineBlockHeight int64) (string, error) { + body := &v1.RewardBody{ + DeadlineBlockHeight: deadlineBlockHeight, + Action: &v1.RewardBody_Delete{Delete: dr}, + } + return r.signAndSendReward(ctx, body) } func (r *Rewards) GetReward(ctx context.Context, address string) (*v1.GetRewardResponse, error) { @@ -113,6 +129,96 @@ func (r *Rewards) GetRewards(ctx context.Context, claim_authority string) (*v1.G return resp.Msg, nil } +// CreateRewardPool submits a CreateRewardPool transaction. The pool is +// keyed by msg.RewardsManagerPubkey (the Solana reward manager pubkey it +// will govern, base58 32 bytes); subsequent SetRewardPoolAuthorities / +// CreateReward calls reference the pool by this same value. +// +// rmKey is the ed25519 PRIVATE key whose public key is +// msg.RewardsManagerPubkey — i.e., the Solana RM state account's +// keypair. For launchpad-minted coins, the relay derives this from +// (launchpadDeterministicSecret, mint). The validator requires the +// envelope-level rm_owner_signature to verify against +// msg.RewardsManagerPubkey, so possession of the matching ed25519 secret +// is the proof-of-RM-control that defeats pool-creation frontrunning. +// +// Returns the cometbft tx hash. +func (r *Rewards) CreateRewardPool(ctx context.Context, msg *v1.CreateRewardPool, rmKey ed25519.PrivateKey, deadlineBlockHeight int64) (string, error) { + // ed25519.Sign panics on wrong-length keys; surface as a typed error + // instead so callers passing nil / hex-decoded-wrong / public-key-by- + // mistake see a recoverable failure rather than a runtime crash. + if len(rmKey) != ed25519.PrivateKeySize { + return "", fmt.Errorf("rmKey is %d bytes; want ed25519 private key (%d bytes)", len(rmKey), ed25519.PrivateKeySize) + } + body := &v1.RewardPoolBody{ + DeadlineBlockHeight: deadlineBlockHeight, + Action: &v1.RewardPoolBody_Create{Create: msg}, + } + bodyBytes, err := common.ProtoSignableBytes(body) + if err != nil { + return "", err + } + rmSig := ed25519.Sign(rmKey, bodyBytes) + return r.signAndSendRewardPool(ctx, body, rmSig) +} + +// GetRewardPool fetches a pool by its rewards_manager_pubkey (Solana RM +// pubkey, base58, 32 bytes). +func (r *Rewards) GetRewardPool(ctx context.Context, rewardsManagerPubkey string) (*v1.GetRewardPoolResponse, error) { + resp, err := r.core.GetRewardPool(ctx, connect.NewRequest(&v1.GetRewardPoolRequest{RewardsManagerPubkey: rewardsManagerPubkey})) + if err != nil { + return nil, err + } + return resp.Msg, nil +} + +// SetRewardPoolAuthorities replaces the pool's authority set wholesale. The +// caller composes the desired list (current minus the one to remove, current +// plus the one to add, etc.); add and remove are derived views. No RM +// keypair signature is needed — rotation is gated by signer ∈ current +// pool authorities, by design (the launchpad needn't be online to rotate). +func (r *Rewards) SetRewardPoolAuthorities(ctx context.Context, msg *v1.SetRewardPoolAuthorities, deadlineBlockHeight int64) (string, error) { + body := &v1.RewardPoolBody{ + DeadlineBlockHeight: deadlineBlockHeight, + Action: &v1.RewardPoolBody_SetAuthorities{SetAuthorities: msg}, + } + return r.signAndSendRewardPool(ctx, body, nil) +} + +// GetRewardSenderAttestation requests an attestation that the validator will +// sign authorizing addr to be added as a sender on the Solana reward manager +// account identified by rewardsManagerPubkey. For pool-managed RMs, the +// validator signs iff addr ∈ pool.authorities; for unmanaged RMs (notably +// AUDIO), the validator falls back to its validator/AAO trust set. The +// returned attestation is meant to be combined with attestations from other +// validators and submitted to Solana via CreateSenderPublic. +func (r *Rewards) GetRewardSenderAttestation(ctx context.Context, addr string, rewardsManagerPubkey string) (*v1.GetRewardSenderAttestationResponse, error) { + resp, err := r.core.GetRewardSenderAttestation(ctx, connect.NewRequest(&v1.GetRewardSenderAttestationRequest{ + Address: addr, + RewardsManagerPubkey: rewardsManagerPubkey, + })) + if err != nil { + return nil, err + } + return resp.Msg, nil +} + +// GetDeleteRewardSenderAttestation is the inverse of GetRewardSenderAttestation: +// the validator signs an attestation authorizing the removal of addr as a +// sender on the Solana RM. For pool-managed RMs, the validator signs iff +// addr ∉ pool.authorities (proving OAP-side rotation already happened). +// Used to deregister leaked / rotated-out keys from Solana. +func (r *Rewards) GetDeleteRewardSenderAttestation(ctx context.Context, addr string, rewardsManagerPubkey string) (*v1.GetDeleteRewardSenderAttestationResponse, error) { + resp, err := r.core.GetDeleteRewardSenderAttestation(ctx, connect.NewRequest(&v1.GetDeleteRewardSenderAttestationRequest{ + Address: addr, + RewardsManagerPubkey: rewardsManagerPubkey, + })) + if err != nil { + return nil, err + } + return resp.Msg, nil +} + func (r *Rewards) GetRewardAttestation(ctx context.Context, req *v1.GetRewardAttestationRequest) (*v1.GetRewardAttestationResponse, error) { // Create a RewardClaim to compile the data in the correct format claim := pkgrewards.RewardClaim{ diff --git a/proto/core/v1/service.proto b/proto/core/v1/service.proto index c7764930..6975e98b 100644 --- a/proto/core/v1/service.proto +++ b/proto/core/v1/service.proto @@ -27,6 +27,7 @@ service CoreService { rpc GetReward(GetRewardRequest) returns (GetRewardResponse) {} rpc GetRewards(GetRewardsRequest) returns (GetRewardsResponse) {} + rpc GetRewardPool(GetRewardPoolRequest) returns (GetRewardPoolResponse) {} rpc GetRewardAttestation(GetRewardAttestationRequest) returns (GetRewardAttestationResponse) {} rpc GetRewardSenderAttestation(GetRewardSenderAttestationRequest) returns (GetRewardSenderAttestationResponse) {} rpc GetDeleteRewardSenderAttestation(GetDeleteRewardSenderAttestationRequest) returns (GetDeleteRewardSenderAttestationResponse) {} diff --git a/proto/core/v1/types.proto b/proto/core/v1/types.proto index 96a6725c..474d17fc 100644 --- a/proto/core/v1/types.proto +++ b/proto/core/v1/types.proto @@ -289,6 +289,7 @@ message SignedTransaction { ddex.v1beta1.NewReleaseMessage release = 1008; RewardMessage reward = 1009; FileUpload file_upload = 1010; + RewardPoolMessage reward_pool = 1011; } } @@ -514,7 +515,29 @@ message GetPIEResponse { ddex.v1beta1.PieMessage pie = 1; } +// RewardMessage is the wire envelope: it bundles a signed body with its +// signature. The body is signed by first taking the deterministic protobuf +// marshaling of body, then computing sha256 over those bytes, and signing +// that digest; the signature lives alongside the body, not inside it, so +// signing has no chicken-and-egg. message RewardMessage { + RewardBody body = 1; + // secp256k1 (Ethereum-style) signature, hex-encoded. The signed + // digest is sha256(deterministic-protobuf-marshaling of body) — i.e., + // marshal body with proto.MarshalOptions{Deterministic: true}, then + // sha256-hash, then crypto.Sign as eth-recoverable. Non-Go clients + // must reproduce this pre-hash (sha256, NOT keccak256) to produce a + // signature the validator will accept. + string signature = 2; +} + +// RewardBody is the signed payload. deadline_block_height bounds the +// signature's validity (the validator rejects after expiry); the action +// is the actual operation. The oneof tag discriminates Create from +// Delete so two actions with otherwise-identical inner shapes produce +// different bytes. +message RewardBody { + int64 deadline_block_height = 1; oneof action { CreateReward create = 1000; DeleteReward delete = 1001; @@ -525,15 +548,142 @@ message CreateReward { string reward_id = 1; string name = 2; uint64 amount = 3; - repeated ClaimAuthority claim_authorities = 4; - int64 deadline_block_height = 5; - string signature = 6; // Signature over deterministic reward data + // Tags 4–6 are reserved: previously held claim_authorities (4), + // deadline_block_height (5), and signature (6) on the pre-pool + // CreateReward shape. The wire-compat layer routes legacy bytes + // through LegacyCreateReward (which preserves those tags), so the + // new CreateReward never decodes legacy data — but reusing those + // tag numbers for new fields would still be a footgun for any + // future tool that bypasses the wire-compat layer or any refactor + // that drops it. Reserve them. + reserved 4, 5, 6; + reserved "claim_authorities", "deadline_block_height", "signature"; + // Solana reward manager pubkey (base58, 32 bytes). Identifies the pool + // whose authorities are allowed to issue rewards in this RM. The + // recovered signer must be a current member of pool.authorities; the + // reward inherits attestation rights from the pool going forward. + // The pool itself is identified by this same pubkey: pool address == + // rewards_manager_pubkey for first-class pools. + string rewards_manager_pubkey = 7; } message DeleteReward { + // Tags 2–3 are reserved: previously held deadline_block_height (2) + // and signature (3) on the pre-pool DeleteReward shape (now + // preserved on LegacyDeleteReward). + reserved 2, 3; + reserved "deadline_block_height", "signature"; string address = 1; // The deployed reward address +} + +// RewardPoolMessage is the wire envelope for pool-management transactions. +// Two signatures cover the same body bytes (`deterministic-protobuf- +// marshaling of body`); the schemes differ in pre-hashing: +// +// - signature: secp256k1 (eth) recoverable signature, hex-encoded. +// Pre-hash is sha256(body_bytes) — see RewardMessage.signature for +// the cross-language reproduction recipe. Identifies the fee-paying +// tx submitter who must be in the pool's current authority set +// (Create: initial authorities; SetAuthorities: existing pool +// authorities). +// - rm_owner_signature: ed25519 signature over body_bytes directly +// (ed25519 handles its own internal hashing — sign body_bytes raw, +// do NOT pre-hash). REQUIRED when body.action is Create. +// Verification key IS body.create.rewards_manager_pubkey (the +// Solana RM state account is itself an ed25519 keypair). Proves +// possession of the RM keypair, defeating pool-creation +// frontrunning by an observer who watches Solana RM init events +// but lacks the launchpad's deterministic-secret. Ignored for +// SetAuthorities because rotation is meant to work without the +// launchpad. +message RewardPoolMessage { + RewardPoolBody body = 1; + string signature = 2; + bytes rm_owner_signature = 3; +} + +// RewardPoolBody is the signed payload. deadline_block_height bounds the +// signature's validity; action discriminates Create from SetAuthorities +// so two otherwise-identical messages produce different bytes. +// +// Cross-chain replay isn't a threat surface here: each environment's +// launchpad uses a different deterministic secret, so the same +// rewards_manager_pubkey cannot be derived on more than one chain. A +// captured CreateRewardPool replayed on a different chain refers to an +// RM that doesn't exist there. +message RewardPoolBody { + int64 deadline_block_height = 1; + oneof action { + CreateRewardPool create = 2000; + SetRewardPoolAuthorities set_authorities = 2001; + } +} + +message CreateRewardPool { + // The pool is identified by its Solana reward manager pubkey (base58, + // 32 bytes). Subsequent CreateReward / SetRewardPoolAuthorities messages + // and the validator's sender-attestation gate use this same value to + // resolve the pool. There is no separate "pool address" — the pool's + // identity IS the RM pubkey, so pool↔RM binding cannot be set wrong + // by construction. + string rewards_manager_pubkey = 1; + // Initial set of eth addresses authorized to attest for rewards in this + // pool. The recovered signer must be a member of this list. Each entry + // must be a valid eth hex address. Frontrunning defense lives at the + // envelope level (RewardPoolMessage.rm_owner_signature). + repeated string authorities = 2; +} + +// SetRewardPoolAuthorities replaces the pool's authority set wholesale. The +// signer must be in the *current* pool.authorities; the new list must be +// non-empty and contain only valid eth addresses (otherwise the pool is +// permanently orphaned). Add and remove are derived views: callers compose +// the new list themselves. +message SetRewardPoolAuthorities { + string rewards_manager_pubkey = 1; + repeated string authorities = 2; +} + +// === Legacy reward wire format (pre-pool-rollout) === +// +// These messages are the on-the-wire shape used before the body+signature +// envelope was introduced. They are NOT used by new clients or new code paths +// — they exist solely so that the new binary can decode and apply historical +// reward transactions encountered during block-sync-from-genesis. +// +// DO NOT REMOVE these types or their field tags: removing them would break +// replay of any chain history that contains rewards committed before the +// upgrade. Adding new fields here is also forbidden — the wire shape must +// remain pinned to what the old network produced. +message LegacyRewardMessage { + oneof action { + LegacyCreateReward create = 1000; + LegacyDeleteReward delete = 1001; + } +} + +message LegacyCreateReward { + string reward_id = 1; + string name = 2; + uint64 amount = 3; + repeated ClaimAuthority claim_authorities = 4; + int64 deadline_block_height = 5; + string signature = 6; +} + +message LegacyDeleteReward { + string address = 1; int64 deadline_block_height = 2; - string signature = 3; // Signature over deterministic delete data + string signature = 3; +} + +message GetRewardPoolRequest { + string rewards_manager_pubkey = 1; +} + +message GetRewardPoolResponse { + string rewards_manager_pubkey = 1; + repeated string authorities = 2; } message GetRewardRequest {