Skip to content

Commit

Permalink
chore(data-proxy): add registration message handling
Browse files Browse the repository at this point in the history
Also introduces an admin address to make updates to the proxy config
easier for operators. An alternative would be to use a sequence number,
but that makes the whole managamenet process more cumbersome for the
operator.

Part-of: #316
  • Loading branch information
Thomasvdam committed Aug 16, 2024
1 parent 26927e1 commit 227ffa2
Show file tree
Hide file tree
Showing 10 changed files with 523 additions and 222 deletions.
5 changes: 4 additions & 1 deletion proto/sedachain/data_proxy/v1/data_proxy.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ message ProxyConfig {
// memo defines an optional string which is not used by the protocol.
string memo = 3;

// only the admin address of a data proxy can submit config updates.
string admin_address = 4;

// fee_update defines an upcoming fee change which will take effect at a
// future height.
FeeUpdate fee_update = 4 [ (gogoproto.nullable) = true ];
FeeUpdate fee_update = 5 [ (gogoproto.nullable) = true ];
}

// FeeUpdate defines a new fee amount and the height at which it will take
Expand Down
27 changes: 14 additions & 13 deletions proto/sedachain/data_proxy/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,28 @@ service Msg {

// All data required for a new data proxy.
message MsgRegisterDataProxy {
string payout_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
option (cosmos.msg.v1.signer) = "admin_address";

cosmos.base.v1beta1.Coin fee = 2;
// admin_address is the address that can update the proxy config.
string admin_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

string memo = 3;
// payout_address defines the address to which the data proxy fees should be
// transferred.
string payout_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// fee defines the amount in aseda this data-proxy charges when utilised.
cosmos.base.v1beta1.Coin fee = 3;

// memo defines an optional string which is not used by the protocol.
string memo = 4;

// hex encoded bytes as the expected flow already uses hex encoded bytes to go
// from the CLI to the browser where the transaction is signed.
string pub_key = 4;
string pub_key = 5;

// hex encoded bytes as the expected flow already uses hex encoded bytes to go
// from the CLI to the browser where the transaction is signed.
string signature = 5;
string signature = 6;
}

// No response required.
Expand All @@ -53,14 +62,6 @@ message MsgEditDataProxy {
cosmos.base.v1beta1.Coin new_fee = 3 [ (gogoproto.nullable) = true ];

uint32 fee_update_delay = 4 [ (gogoproto.nullable) = true ];

// hex encoded bytes as the expected flow already uses hex encoded bytes to go
// from the CLI to the browser where the transaction is signed.
string pub_key = 5;

// hex encoded bytes as the expected flow already uses hex encoded bytes to go
// from the CLI to the browser where the transaction is signed.
string signature = 6;
}

// Returns the height at which the fee update will go into effect.
Expand Down
30 changes: 25 additions & 5 deletions x/data-proxy/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sedaprotocol/seda-chain/x/data-proxy/types"
)

const (
// FlagKeyFile defines a flag to add arbitrary data to a data proxy.
FlagMemo = "memo"
)

// GetTxCmd returns the CLI transaction commands for this module
func GetTxCmd() *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -29,24 +35,38 @@ func GetTxCmd() *cobra.Command {
// AddKey returns the command for adding a new key and uploading its
// public key on chain at a given index.
func RegisterDataProxy() *cobra.Command {
// TODO
cmd := &cobra.Command{
Use: "register [thing]",
Use: "register [payout_address] [fee] [public_key_hex] [signature_hex] --from [admin_address]",
Short: "Register a data proxy using a signed message generated with the data-proxy cli",
Args: cobra.ExactArgs(1),
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

// TODO
msg := &types.MsgRegisterDataProxy{}
fee, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}

memo, _ := cmd.Flags().GetString(FlagMemo)

msg := &types.MsgRegisterDataProxy{
AdminAddress: clientCtx.GetFromAddress().String(),
PayoutAddress: args[0],
Fee: &fee,
PubKey: args[2],
Signature: args[3],
Memo: memo,
}

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

flags.AddTxFlagsToCmd(cmd)
cmd.Flags().String(FlagMemo, "", "Optionally add a description to the data proxy config")
return cmd
}

Expand Down
55 changes: 51 additions & 4 deletions x/data-proxy/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,71 @@ package keeper_test
import (
"testing"

"github.com/stretchr/testify/suite"

storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/server"
sdktestutil "github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/stretchr/testify/suite"

"github.com/sedaprotocol/seda-chain/app/params"
dataproxy "github.com/sedaprotocol/seda-chain/x/data-proxy"
"github.com/sedaprotocol/seda-chain/x/data-proxy/keeper"
"github.com/sedaprotocol/seda-chain/x/data-proxy/types"
)

type KeeperTestSuite struct {
suite.Suite
ctx sdk.Context
keeper *keeper.Keeper
ctx sdk.Context
keeper *keeper.Keeper
cdc codec.Codec
msgSrvr types.MsgServer
queryClient types.QueryClient
serverCtx *server.Context
authority string
}

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}

func (s *KeeperTestSuite) SetupSuite() {
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(params.Bech32PrefixAccAddr, params.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(params.Bech32PrefixValAddr, params.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(params.Bech32PrefixConsAddr, params.Bech32PrefixConsPub)
config.Seal()
}

func (s *KeeperTestSuite) SetupTest() {
t := s.T()
t.Helper()

s.authority = authtypes.NewModuleAddress("gov").String()

key := storetypes.NewKVStoreKey(types.StoreKey)
testCtx := sdktestutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(dataproxy.AppModuleBasic{})
types.RegisterInterfaces(encCfg.InterfaceRegistry)

s.keeper = keeper.NewKeeper(
encCfg.Codec,
runtime.NewKVStoreService(key),
s.authority,
)
s.ctx = testCtx.Ctx
s.cdc = encCfg.Codec
s.serverCtx = server.NewDefaultContext()

msr := keeper.NewMsgServerImpl(*s.keeper)
s.msgSrvr = msr

queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, encCfg.InterfaceRegistry)
querier := keeper.Querier{Keeper: *s.keeper}
types.RegisterQueryServer(queryHelper, querier)
s.queryClient = types.NewQueryClient(queryHelper)
}
59 changes: 57 additions & 2 deletions x/data-proxy/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package keeper

import (
"context"
"encoding/hex"
"fmt"

"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sedaprotocol/seda-chain/x/data-proxy/types"
Expand All @@ -21,8 +25,59 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{Keeper: keeper}
}

func (m msgServer) RegisterDataProxy(_ context.Context, _ *types.MsgRegisterDataProxy) (*types.MsgRegisterDataProxyResponse, error) {
// TODO
func (m msgServer) RegisterDataProxy(goCtx context.Context, msg *types.MsgRegisterDataProxy) (*types.MsgRegisterDataProxyResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

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

if _, err := sdk.AccAddressFromBech32(msg.AdminAddress); err != nil {
return nil, types.ErrInvalidAddress.Wrapf("invalid admin address %s", err)
}

pubKeyBytes, err := hex.DecodeString(msg.PubKey)
if err != nil {
return nil, types.ErrInvalidHex.Wrap(err.Error())
}

signatureBytes, err := hex.DecodeString(msg.Signature)
if err != nil {
return nil, types.ErrInvalidHex.Wrap(err.Error())
}

found, err := m.DataProxyConfigs.Has(ctx, pubKeyBytes)
if err != nil {
return nil, err
}
if found {
return nil, types.ErrAlreadyExists
}

feeBytes := []byte(msg.Fee.String())
payoutAddressBytes := []byte(msg.PayoutAddress)
memoBytes := []byte(msg.Memo)

payload := make([]byte, 0, len(feeBytes)+len(payoutAddressBytes)+len(memoBytes))
payload = append(payload, feeBytes...)
payload = append(payload, payoutAddressBytes...)
payload = append(payload, memoBytes...)

if valid := secp256k1.VerifySignature(pubKeyBytes, crypto.Keccak256(payload), signatureBytes); !valid {
return nil, types.ErrInvalidSignature
}

err = m.DataProxyConfigs.Set(ctx, pubKeyBytes, types.ProxyConfig{
PayoutAddress: msg.PayoutAddress,
Fee: msg.Fee,
Memo: msg.Memo,
FeeUpdate: nil,
AdminAddress: msg.AdminAddress,
})
if err != nil {
return nil, err
}

return &types.MsgRegisterDataProxyResponse{}, nil
}

Expand Down
Loading

0 comments on commit 227ffa2

Please sign in to comment.