Skip to content

Commit

Permalink
feat: store lightnode activations (#1309)
Browse files Browse the repository at this point in the history
# Related Github tickets

- Closes #2250
- Closes #2251
- Closes #2252

# Background

Keep an on-chain list of activated lightnodes. When a new node is
activated, we add it to the list. To bootstrap the list with the
existing lightnodes, we add a new transaction. This is instead of a
governance vote, so we don't waste more time. The transaction can be
executed at any time after the upgrade.
For each lightnode we keep track of: address, activated timestamp, last
authentication timestamp. This way we can also know which nodes are
active and performing authentications.

# Testing completed

- [ ] test coverage exists or has been added/updated
- [x] tested in a private testnet

# Breaking changes

- [x] I have checked my code for breaking changes
- [x] If there are breaking changes, there is a supporting migration.
  • Loading branch information
maharifu authored Oct 8, 2024
1 parent f702f11 commit 49c31df
Show file tree
Hide file tree
Showing 21 changed files with 1,741 additions and 110 deletions.
2 changes: 2 additions & 0 deletions proto/palomachain/paloma/paloma/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";
package palomachain.paloma.paloma;

import "gogoproto/gogo.proto";
import "palomachain/paloma/paloma/light_node_client.proto";
import "palomachain/paloma/paloma/light_node_client_feegranter.proto";
import "palomachain/paloma/paloma/light_node_client_funders.proto";
import "palomachain/paloma/paloma/light_node_client_license.proto";
Expand All @@ -15,4 +16,5 @@ message GenesisState {
repeated LightNodeClientLicense light_node_client_licenses = 2;
LightNodeClientFeegranter light_node_client_feegranter = 3;
LightNodeClientFunders light_node_client_funders = 4;
repeated LightNodeClient light_node_clients = 5;
}
17 changes: 17 additions & 0 deletions proto/palomachain/paloma/paloma/light_node_client.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
syntax = "proto3";
package palomachain.paloma.paloma;

import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/palomachain/paloma/v2/x/paloma/types";

message LightNodeClient {
string client_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
google.protobuf.Timestamp activated_at = 2
[ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ];
google.protobuf.Timestamp last_auth_at = 3
[ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ];
}
13 changes: 12 additions & 1 deletion proto/palomachain/paloma/paloma/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "cosmos/base/query/v1beta1/pagination.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "palomachain/paloma/paloma/light_node_client.proto";
import "palomachain/paloma/paloma/light_node_client_feegranter.proto";
import "palomachain/paloma/paloma/light_node_client_funders.proto";
import "palomachain/paloma/paloma/light_node_client_license.proto";
Expand Down Expand Up @@ -34,7 +35,13 @@ service Query {
rpc GetLightNodeClientFunders(google.protobuf.Empty)
returns (QueryLightNodeClientFundersResponse) {
option (google.api.http).get =
"/palomachain/paloma/paloma/light_node_client_funder";
"/palomachain/paloma/paloma/light_node_client_funders";
}

rpc GetLightNodeClients(google.protobuf.Empty)
returns (QueryLightNodeClientsResponse) {
option (google.api.http).get =
"/palomachain/paloma/paloma/light_node_clients";
}
}

Expand All @@ -59,3 +66,7 @@ message QueryLightNodeClientFeegranterResponse {
message QueryLightNodeClientFundersResponse {
LightNodeClientFunders light_node_client_funders = 1;
}

message QueryLightNodeClientsResponse {
repeated LightNodeClient light_node_clients = 1;
}
18 changes: 18 additions & 0 deletions proto/palomachain/paloma/paloma/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ service Msg {

rpc AddLightNodeClientLicense(MsgAddLightNodeClientLicense)
returns (EmptyResponse);

rpc AuthLightNodeClient(MsgAuthLightNodeClient)
returns (EmptyResponse);

rpc SetLegacyLightNodeClients(MsgSetLegacyLightNodeClients)
returns (EmptyResponse);
}

message MsgAddStatusUpdate {
Expand Down Expand Up @@ -57,3 +63,15 @@ message MsgAddLightNodeClientLicense {
cosmos.base.v1beta1.Coin amount = 3 [ (gogoproto.nullable) = false ];
uint32 vesting_months = 4;
}

message MsgAuthLightNodeClient {
option (cosmos.msg.v1.signer) = "metadata";
palomachain.paloma.valset.MsgMetadata metadata = 1
[ (gogoproto.nullable) = false ];
}

message MsgSetLegacyLightNodeClients {
option (cosmos.msg.v1.signer) = "metadata";
palomachain.paloma.valset.MsgMetadata metadata = 1
[ (gogoproto.nullable) = false ];
}
1 change: 1 addition & 0 deletions x/paloma/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func GetQueryCmd(queryRoute string) *cobra.Command {
cmd.AddCommand(CmdQueryLightNodeClientFeegranter())
cmd.AddCommand(CmdQueryLightNodeClientLicenses())
cmd.AddCommand(CmdQueryLightNodeClientFunders())
cmd.AddCommand(CmdQueryLightNodeClients())

return cmd
}
37 changes: 37 additions & 0 deletions x/paloma/client/cli/query_light_node_clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cli

import (
"context"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/palomachain/paloma/v2/x/paloma/types"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/emptypb"
)

func CmdQueryLightNodeClients() *cobra.Command {
cmd := &cobra.Command{
Use: "light-node-clients",
Short: "Shows information about all activated lightnode clients",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)

queryClient := types.NewQueryClient(clientCtx)

params := &emptypb.Empty{}
res, err := queryClient.GetLightNodeClients(
context.Background(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
68 changes: 68 additions & 0 deletions x/paloma/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func GetTxCmd() *cobra.Command {

cmd.AddCommand(CmdRegisterLightNodeClient())
cmd.AddCommand(CmdAddLightNodeClientLicense())
cmd.AddCommand(CmdAuthLightNodeClient())
cmd.AddCommand(CmdSetLegacyLightNodeClients())

return cmd
}
Expand Down Expand Up @@ -125,3 +127,69 @@ The [vesting-months] parameter determines how long the funds will take to fully

return cmd
}

func CmdAuthLightNodeClient() *cobra.Command {
cmd := &cobra.Command{
Use: "auth-light-node-client",
Short: "Authenticate the lightnode using the `from` address",
Long: "Authenticate the lightnode using the `from` address",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

creator := clientCtx.GetFromAddress().String()
msg := &types.MsgAuthLightNodeClient{
Metadata: valsettypes.MsgMetadata{
Creator: creator,
Signers: []string{creator},
},
}

if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

func CmdSetLegacyLightNodeClients() *cobra.Command {
cmd := &cobra.Command{
Use: "set-legacy-light-node-clients",
Short: "Set the legacy lightnode client addresses from feegranter",
Long: "Set the legacy lightnode client addresses from feegranter",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

creator := clientCtx.GetFromAddress().String()
msg := &types.MsgSetLegacyLightNodeClients{
Metadata: valsettypes.MsgMetadata{
Creator: creator,
Signers: []string{creator},
},
}

if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
12 changes: 12 additions & 0 deletions x/paloma/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisSta
panic(err)
}
}

for _, client := range genState.LightNodeClients {
err := k.SetLightNodeClient(ctx, client.ClientAddress, client)
if err != nil {
panic(err)
}
}
}

// ExportGenesis returns the capability module's exported genesis.
Expand All @@ -60,5 +67,10 @@ func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState {
panic(err)
}

genesis.LightNodeClients, err = k.AllLightNodeClients(ctx)
if err != nil {
panic(err)
}

return genesis
}
20 changes: 20 additions & 0 deletions x/paloma/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package paloma_test

import (
"testing"
"time"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -91,4 +92,23 @@ func TestGenesis(t *testing.T) {
require.Equal(t, genesisState.LightNodeClientFunders.Accounts,
got.LightNodeClientFunders.Accounts)
})

t.Run("With light node clients", func(t *testing.T) {
genesisState := types.GenesisState{
LightNodeClients: []*types.LightNodeClient{
{
ClientAddress: clientAddr,
ActivatedAt: time.Now().UTC(),
LastAuthAt: time.Now().UTC(),
},
},
}

k, ctx := keepertest.PalomaKeeper(t)
paloma.InitGenesis(ctx, *k, genesisState)
got := paloma.ExportGenesis(ctx, *k)
require.NotNil(t, got)

require.Equal(t, genesisState.LightNodeClients, got.LightNodeClients)
})
}
25 changes: 25 additions & 0 deletions x/paloma/keeper/grpc_query_light_node_clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/v2/x/paloma/types"
"google.golang.org/protobuf/types/known/emptypb"
)

func (k Keeper) GetLightNodeClients(
c context.Context,
_ *emptypb.Empty,
) (*types.QueryLightNodeClientsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)

clients, err := k.AllLightNodeClients(ctx)
if err != nil {
return nil, err
}

return &types.QueryLightNodeClientsResponse{
LightNodeClients: clients,
}, nil
}
Loading

0 comments on commit 49c31df

Please sign in to comment.