Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AppGate] Implement the MVP AppGateServer #108

Merged
merged 42 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cec3ef4
feat: implement app client
red-0ne Oct 27, 2023
d312423
Merge remote-tracking branch 'origin/main' into feat/app-client
red-0ne Nov 6, 2023
c4fe2da
chore: address review comments
red-0ne Nov 7, 2023
a6edf61
Merge remote-tracking branch 'origin/main' into feat/app-client
red-0ne Nov 7, 2023
aab9e17
fix: remove signature field before signing
red-0ne Nov 7, 2023
0f2a53f
chore: go.mod
h5law Nov 7, 2023
65c524f
Merge branch 'main' into feat/app-client
h5law Nov 7, 2023
6e265c2
feat: add ring signatures
h5law Nov 7, 2023
a966b66
Merge branch 'main' into feat/app-client
h5law Nov 7, 2023
80af5fb
Merge branch 'main' into feat/app-client
h5law Nov 9, 2023
c1f6115
chore: remove mock files
h5law Nov 9, 2023
c681484
chore: fix spelling errors
h5law Nov 9, 2023
c787d80
fixup: spelling mistake
h5law Nov 9, 2023
65e9bee
feat: add command to start the appgateserver
h5law Nov 9, 2023
c22dc9c
chore: add debug lines
h5law Nov 9, 2023
c14a8b6
Merge branch 'main' into feat/app-client
h5law Nov 9, 2023
ec57a8e
chore: debugging
h5law Nov 9, 2023
ba3bd8f
Merge branch 'main' into feat/app-client
h5law Nov 9, 2023
781ee13
chore: go.mod
h5law Nov 9, 2023
078be29
chore: close websocket connections
h5law Nov 9, 2023
00211a9
Merge branch 'main' into feat/app-client
h5law Nov 9, 2023
12d97d6
chore: add ws todo
h5law Nov 9, 2023
2591e19
Merge remote-tracking branch 'origin/main' into feat/app-client
red-0ne Nov 10, 2023
3f2cb90
chore: Use depinject for AppGateServer
red-0ne Nov 10, 2023
a857e5f
fix: Get appAddress from url query when appAddress is empty
red-0ne Nov 10, 2023
bc2efb3
feat: address comments
h5law Nov 10, 2023
76a31c0
chore: fix signing key field
h5law Nov 10, 2023
09c843c
chore: defer cancelling ctx
h5law Nov 10, 2023
151313e
chore: cleanup log lines
h5law Nov 10, 2023
2cd04e7
chore: address comments
h5law Nov 10, 2023
5daed4a
chore: address comments
h5law Nov 10, 2023
d8c668a
Merge branch 'main' into feat/app-client
h5law Nov 10, 2023
118d26d
chore: update comments and naming
h5law Nov 10, 2023
ce91371
chore: fix missing if
h5law Nov 10, 2023
969bf0f
chore: add signed relay received debug log
h5law Nov 10, 2023
211df7f
chore: cleanup comments
h5law Nov 10, 2023
89786c3
chore: comments comments comments
h5law Nov 10, 2023
b6f7ef5
chore: comments comments comments
h5law Nov 10, 2023
1d9c233
chore: update ring comments
h5law Nov 10, 2023
89688ab
feat: refactor appgateserver creation with depinject supplier functio…
h5law Nov 10, 2023
683b285
chore: re-add missing signing information check
h5law Nov 10, 2023
35936ac
Merge branch 'main' into feat/app-client
h5law Nov 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmd/pocketd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (

"github.com/pokt-network/poktroll/app"
appparams "github.com/pokt-network/poktroll/app/params"
appgateservercmd "github.com/pokt-network/poktroll/pkg/appgateserver/cmd"
)

// NewRootCmd creates a new root command for a Cosmos SDK application
Expand Down Expand Up @@ -148,6 +149,11 @@ func initRootCmd(
txCommand(),
keys.Commands(app.DefaultNodeHome),
)

// add the appgate server command
rootCmd.AddCommand(
appgateservercmd.AppGateServerCmd(),
)
}

// queryCommand returns the sub-command to send queries to the app
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0-beta.7
cosmossdk.io/math v1.0.1
github.com/athanorlabs/go-dleq v0.1.0
github.com/cometbft/cometbft v0.37.2
github.com/cometbft/cometbft-db v0.8.0
github.com/cosmos/cosmos-proto v1.0.0-beta.2
Expand All @@ -20,6 +21,7 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
github.com/noot/ring-go v0.0.0-20231019173746-6c4b33bcf03f
github.com/pokt-network/smt v0.7.1
github.com/regen-network/gocuke v0.6.2
github.com/spf13/cast v1.5.1
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI=
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
github.com/athanorlabs/go-dleq v0.1.0 h1:0/llWZG8fz2uintMBKOiBC502zCsDA8nt8vxI73W9Qc=
github.com/athanorlabs/go-dleq v0.1.0/go.mod h1:DWry6jSD7A13MKmeZA0AX3/xBeQCXDoygX99VPwL3yU=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
Expand Down Expand Up @@ -1481,6 +1483,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/nishanths/exhaustive v0.8.1/go.mod h1:qj+zJJUgJ76tR92+25+03oYUhzF4R7/2Wk7fGTfCHmg=
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
github.com/noot/ring-go v0.0.0-20231019173746-6c4b33bcf03f h1:1+NP/H13eFAqBYrGpRkbJUWVWIO2Zr2eP7a/q0UtZVQ=
github.com/noot/ring-go v0.0.0-20231019173746-6c4b33bcf03f/go.mod h1:0t3gzoSfW2bkTce1E/Jis3MQpjiKGhAgqieFK+nkQsI=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down
199 changes: 199 additions & 0 deletions pkg/appgateserver/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package cmd

import (
"context"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/signal"

"cosmossdk.io/depinject"
ring_secp256k1 "github.com/athanorlabs/go-dleq/secp256k1"
ringtypes "github.com/athanorlabs/go-dleq/types"
cosmosclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/spf13/cobra"

"github.com/pokt-network/poktroll/pkg/appgateserver"
blockclient "github.com/pokt-network/poktroll/pkg/client/block"
eventsquery "github.com/pokt-network/poktroll/pkg/client/events_query"
)

var (
signingKeyName string
h5law marked this conversation as resolved.
Show resolved Hide resolved
selfSigning bool
listeningEndpoint string
cometWebsocketUrl string
)

func AppGateServerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "appgate-server",
Short: "Starts the AppGate server",
Long: `Starts the AppGate server that will listen for incoming relays requests and will handle
the interaction with the chain, sessions and suppliers in order to receive the correct
response for the request.

If the server is started with a defined --self-signing flag, it will behave
as an application and sign any incoming requests with the private key associated with
the --signing-key-name flag. If however, this flag is not provided, the server will
behave as a gateway and will sign relays on behalf of any application sending it relays provided
that the address recieved in the query parameters of a request has been delegated to by the

Check warning on line 47 in pkg/appgateserver/cmd/cmd.go

View workflow job for this annotation

GitHub Actions / misspell

[misspell] pkg/appgateserver/cmd/cmd.go#L47

"recieved" is a misspelling of "received"
Raw output
./pkg/appgateserver/cmd/cmd.go:47:17: "recieved" is a misspelling of "received"
gateway, this is so that it can sign relays using the ring of the application with the
key associated with the --signing-key-name flag.

If an application doesn't provide the --self-signing flag, it will be able to send relays
to the AppGate server and it will still function as an application, however each request
will have to contain the "?senderAddress=[address]" query parameter, where [address] is
the address of the application that is sending the request. This is so that the server
can generate the correct ring for the application and sign the request.`,
h5law marked this conversation as resolved.
Show resolved Hide resolved
Args: cobra.NoArgs,
RunE: runAppGateServer,
}

cmd.Flags().StringVar(&signingKeyName, "signing-key-name", "", "The name of the key that will be used to sign relays")
cmd.Flags().StringVar(&listeningEndpoint, "listening-endpoint", "http://localhost:42069", "The host and port that the server will listen on")
h5law marked this conversation as resolved.
Show resolved Hide resolved
cmd.Flags().StringVar(&cometWebsocketUrl, "comet-websocket-url", "ws://localhost:36657/websocket", "The URL of the tendermint websocket endpoint to interact with the chain")
h5law marked this conversation as resolved.
Show resolved Hide resolved
cmd.Flags().BoolVar(&selfSigning, "self-signing", false, "Whether the server should sign all incoming requests with its own ring (for applications)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@h5law My understanding was that the presence of signing-key-name is what determines this, no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is not the case, in fact for both gateways and applications they need a key to sign with: signing-key (removed the name)

However, gateways do not "self-sign" instead they "others-sign" but applications do "self-sign". This means that if an appgate server is started and declares it will "self-sign" for every request it will fetch its own ring and sign it that way.

If it doesnt opt to "self sign" for every request it will fetch the ring of the application address it is provided, but it will still have to sign the ring with its signing-key.

Does that clear it up?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I'd say #PUC right next to the flag definitions!


cmd.Flags().String(flags.FlagKeyringBackend, "", "Select keyring's backend (os|file|kwallet|pass|test)")
cmd.Flags().String(flags.FlagNode, "tcp://localhost:36657", "tcp://<host>:<port> to tendermint rpc interface for this chain")
h5law marked this conversation as resolved.
Show resolved Hide resolved

return cmd
}

func runAppGateServer(cmd *cobra.Command, _ []string) error {
// Create a context that is cancelled when the command is interrupted

Check warning on line 72 in pkg/appgateserver/cmd/cmd.go

View workflow job for this annotation

GitHub Actions / misspell

[misspell] pkg/appgateserver/cmd/cmd.go#L72

"cancelled" is a misspelling of "canceled"
Raw output
./pkg/appgateserver/cmd/cmd.go:72:29: "cancelled" is a misspelling of "canceled"
h5law marked this conversation as resolved.
Show resolved Hide resolved
ctx, cancelCtx := context.WithCancel(cmd.Context())

// Retrieve the client context for the chain interactions.
clientCtx := cosmosclient.GetClientContextFromCmd(cmd)

// Parse the listening endpoint.
listeningUrl, err := url.Parse(listeningEndpoint)
if err != nil {
cancelCtx()
return fmt.Errorf("failed to parse listening endpoint: %w", err)
}

// Obtain the tendermint websocket endpoint from the client context.
h5law marked this conversation as resolved.
Show resolved Hide resolved
cometWSUrl, err := url.Parse(clientCtx.NodeURI + "/websocket")
h5law marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
cancelCtx()
return fmt.Errorf("failed to parse block query URL: %w", err)
}
cometWSUrl.Scheme = "ws"
h5law marked this conversation as resolved.
Show resolved Hide resolved
// If the comet websocket URL is not provided, use the one from the client context.
if cometWebsocketUrl == "" {
cometWebsocketUrl = cometWSUrl.String()
}

log.Printf("INFO: Creating block client, using websocket URL: %s...", cometWebsocketUrl)
h5law marked this conversation as resolved.
Show resolved Hide resolved

// Create the block client with its dependency on the events client.
eventsQueryClient := eventsquery.NewEventsQueryClient(cometWebsocketUrl)
deps := depinject.Supply(eventsQueryClient)
blockClient, err := blockclient.NewBlockClient(ctx, deps, cometWebsocketUrl)
if err != nil {
cancelCtx()
return fmt.Errorf("failed to create block client: %w", err)
}

log.Println("INFO: Creating AppGate server...")

key, err := clientCtx.Keyring.Key(signingKeyName)
h5law marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
cancelCtx()
return fmt.Errorf("failed to get key from keyring: %w", err)
}

appAddress, err := key.GetAddress()
if err != nil {
cancelCtx()
return fmt.Errorf("failed to get address from key: %w", err)
}
signingAddress := ""
if selfSigning {
signingAddress = appAddress.String()
}

signingKey, err := recordLocalToScalar(key.GetLocal())
h5law marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
cancelCtx()
return fmt.Errorf("failed to convert private key to scalar: %w", err)
}
signingKey = signingKey
h5law marked this conversation as resolved.
Show resolved Hide resolved
signingInfo := appgateserver.SigningInformation{
h5law marked this conversation as resolved.
Show resolved Hide resolved
SigningKey: signingKey,
AppAddress: signingAddress,
}

// Create the AppGate server.
appGateServerDeps := depinject.Supply(
clientCtx,
blockClient,
)

appGateServer, err := appgateserver.NewAppGateServer(
appGateServerDeps,
appgateserver.WithSigningInformation(&signingInfo),
appgateserver.WithListeningUrl(listeningUrl),
)
if err != nil {
cancelCtx()
return fmt.Errorf("failed to create AppGate server: %w", err)
}

// Handle interrupts in a goroutine.
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)

// Block until we receive an interrupt or kill signal (OS-agnostic)
<-sigCh
log.Println("INFO: Interrupt signal received, shutting down...")

// Signal goroutines to stop
cancelCtx()
}()

log.Printf("INFO: Starting AppGate server, listening on %s...", listeningUrl.String())

// Start the AppGate server.
if err := appGateServer.Start(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
cancelCtx()
return fmt.Errorf("failed to start app gate server: %w", err)
} else if errors.Is(err, http.ErrServerClosed) {
cancelCtx()
log.Println("INFO: AppGate server stopped")
}

return nil
}

// recordLocalToScalar converts the private key obtained from a
// key record to a scalar point on the secp256k1 curve
func recordLocalToScalar(local *keyring.Record_Local) (ringtypes.Scalar, error) {
if local == nil {
return nil, fmt.Errorf("cannot extract private key from key record: nil")
}
priv, ok := local.PrivKey.GetCachedValue().(cryptotypes.PrivKey)
if !ok {
return nil, fmt.Errorf("cannot extract private key from key record: %T", local.PrivKey.GetCachedValue())
}
if _, ok := priv.(*secp256k1.PrivKey); !ok {
return nil, fmt.Errorf("unexpected private key type: %T, want %T", priv, &secp256k1.PrivKey{})
}
crv := ring_secp256k1.NewCurve()
privKey, err := crv.DecodeToScalar(priv.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode private key: %w", err)
}
return privKey, nil
}
45 changes: 45 additions & 0 deletions pkg/appgateserver/endpoint_selector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package appgateserver

import (
"context"
"log"
"net/url"

sessiontypes "github.com/pokt-network/poktroll/x/session/types"
sharedtypes "github.com/pokt-network/poktroll/x/shared/types"
)

// TODO_IMPROVE: This implements a naive greedy approach that defaults to the
// first available URL but future optimizations can be introduced.
h5law marked this conversation as resolved.
Show resolved Hide resolved
// TODO(@h5law): Look into different endpoint selection depending on their suitability.
// getRelayerUrl gets the URL of the relayer for the given service.
func (app *appGateServer) getRelayerUrl(
h5law marked this conversation as resolved.
Show resolved Hide resolved
ctx context.Context,
serviceId string,
rpcType sharedtypes.RPCType,
session *sessiontypes.Session,
) (supplierUrl *url.URL, supplierAddress string, err error) {
for _, supplier := range session.Suppliers {
for _, service := range supplier.Services {
// Skip services that don't match the requested serviceId.
if service.Service.Id != serviceId {
continue
}

for _, endpoint := range service.Endpoints {
// Return the first endpoint url that matches the JSON RPC RpcType.
if endpoint.RpcType == rpcType {
supplierUrl, err := url.Parse(endpoint.Url)
if err != nil {
log.Printf("error parsing url: %s", err)
continue
}
return supplierUrl, supplier.Address, nil
}
}
}
}

// Return an error if no relayer endpoints were found.
return nil, "", ErrAppGateNoRelayEndpoints
}
13 changes: 13 additions & 0 deletions pkg/appgateserver/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package appgateserver

import sdkerrors "cosmossdk.io/errors"

var (
codespace = "appgateserver"
ErrAppGateInvalidRelayResponseSignature = sdkerrors.Register(codespace, 1, "invalid relay response signature")
ErrAppGateNoRelayEndpoints = sdkerrors.Register(codespace, 2, "no relay endpoints found")
ErrAppGateInvalidRequestURL = sdkerrors.Register(codespace, 3, "invalid request URL")
ErrAppGateMissingAppAddress = sdkerrors.Register(codespace, 4, "missing application address")
ErrAppGateMissingSigningInformation = sdkerrors.Register(codespace, 5, "missing app client signing information")
ErrAppGateMissingListeningEndpoint = sdkerrors.Register(codespace, 6, "missing app client listening endpoint")
)
Loading