-
Notifications
You must be signed in to change notification settings - Fork 115
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
[CT-646] stream offchain updates through stream manager #1138
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -961,7 +961,9 @@ func New( | |
clobFlags := clobflags.GetClobFlagValuesFromOptions(appOpts) | ||
logger.Info("Parsed CLOB flags", "Flags", clobFlags) | ||
|
||
memClob := clobmodulememclob.NewMemClobPriceTimePriority(app.IndexerEventManager.Enabled()) | ||
memClob := clobmodulememclob.NewMemClobPriceTimePriority( | ||
app.IndexerEventManager.Enabled() || app.GrpcStreamingManager.Enabled(), | ||
) | ||
|
||
app.ClobKeeper = clobmodulekeeper.NewKeeper( | ||
appCodec, | ||
|
@@ -1758,8 +1760,5 @@ func getGrpcStreamingManagerFromOptions( | |
logger log.Logger, | ||
) (manager streamingtypes.GrpcStreamingManager) { | ||
// TODO(CT-625): add command line flags for full node streaming. | ||
if appFlags.NonValidatingFullNode { | ||
return streaming.NewGrpcStreamingManager() | ||
} | ||
return streaming.NewNoopGrpcStreamingManager() | ||
return streaming.NewGrpcStreamingManager() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The gRPC server is initialized without specifying credentials, which could lead to insecure communication. It's important to ensure that all gRPC communication is encrypted to prevent eavesdropping and tampering by malicious actors. Consider adding SSL/TLS credentials to the gRPC server initialization. You can create credentials using grpc.NewServer(grpc.Creds(credentials.NewServerTLSFromFile("cert.pem", "cert.key"))) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately the ec2 instance I am running this on does not support filesystem reads and writes. Can I paste the contents of my .pem file in the code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jonfung-dydx Embedding the contents of your .pem file directly in the code is not recommended due to security concerns. A more secure approach would be to use environment variables or a secrets management service provided by your cloud provider. This way, you can securely manage the .pem file contents without exposing them in your source code. For example, you could load the .pem file contents into an environment variable and then access it in your code like so: cert := os.Getenv("CERT_PEM_CONTENTS")
key := os.Getenv("KEY_PEM_CONTENTS")
creds, err := credentials.NewServerTLSFromString(cert, key)
if err != nil {
// Handle error
}
grpcServer := grpc.NewServer(grpc.Creds(creds)) Make sure to securely set the environment variables There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I have no money to use AWS secrets manager. What should I do here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jonfung-dydx If AWS Secrets Manager is not an option due to cost concerns, you might consider the following approaches to securely manage your .pem file contents for the gRPC server:
Each of these methods has its own set of considerations, especially regarding the secure handling of encryption keys and secret values. It's important to evaluate them in the context of your application's security requirements and operational environment. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
package grpc | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/cosmos/gogoproto/proto" | ||
ocutypes "github.com/dydxprotocol/v4-chain/protocol/indexer/off_chain_updates/types" | ||
"github.com/dydxprotocol/v4-chain/protocol/streaming/grpc/types" | ||
clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" | ||
) | ||
|
@@ -9,30 +13,111 @@ var _ types.GrpcStreamingManager = (*GrpcStreamingManagerImpl)(nil) | |
|
||
// GrpcStreamingManagerImpl is an implementation for managing gRPC streaming subscriptions. | ||
type GrpcStreamingManagerImpl struct { | ||
sync.Mutex | ||
jonfung-dydx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// orderbookSubscriptions maps subscription IDs to their respective orderbook subscriptions. | ||
orderbookSubscriptions map[uint32]*OrderbookSubscription | ||
nextId uint32 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
} | ||
|
||
// OrderbookSubscription represents a active subscription to the orderbook updates stream. | ||
type OrderbookSubscription struct { | ||
clobPairIds []uint32 | ||
srv clobtypes.Query_StreamOrderbookUpdatesServer | ||
finished chan bool | ||
} | ||
|
||
func NewGrpcStreamingManager() *GrpcStreamingManagerImpl { | ||
return &GrpcStreamingManagerImpl{} | ||
return &GrpcStreamingManagerImpl{ | ||
orderbookSubscriptions: make(map[uint32]*OrderbookSubscription), | ||
} | ||
} | ||
|
||
func (sm *GrpcStreamingManagerImpl) Enabled() bool { | ||
return true | ||
} | ||
|
||
// Subscribe subscribes to the orderbook updates stream. | ||
// This function returns a channel that is used to signal termination when an error occurs. | ||
jayy04 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func (sm *GrpcStreamingManagerImpl) Subscribe( | ||
req clobtypes.StreamOrderbookUpdatesRequest, | ||
srv clobtypes.Query_StreamOrderbookUpdatesServer, | ||
) ( | ||
finished chan bool, | ||
err error, | ||
) { | ||
return nil, nil | ||
finished = make(chan bool) | ||
subscription := &OrderbookSubscription{ | ||
clobPairIds: req.GetClobPairId(), | ||
srv: srv, | ||
finished: finished, | ||
} | ||
|
||
sm.Lock() | ||
defer sm.Unlock() | ||
|
||
sm.orderbookSubscriptions[sm.nextId] = subscription | ||
sm.nextId++ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for my understanding, what is the point of the nextId variable if we are just using this as a unique key variable for a map? seems like we don't expose it anywhere here, where are we gonna expose subscription key? functionally this is the same as an append only array EDIT: i see, it is for removal |
||
|
||
return finished, nil | ||
} | ||
|
||
// SendOrderbookUpdates groups updates by their clob pair ids and | ||
// sends messages to the subscribers. | ||
func (sm *GrpcStreamingManagerImpl) SendOrderbookUpdates( | ||
updates *clobtypes.OffchainUpdates, | ||
offchainUpdates *clobtypes.OffchainUpdates, | ||
) { | ||
// Group updates by clob pair id. | ||
updates := make(map[uint32]*clobtypes.OffchainUpdates) | ||
for _, message := range offchainUpdates.Messages { | ||
clobPairId := message.OrderId.ClobPairId | ||
if _, ok := updates[clobPairId]; !ok { | ||
updates[clobPairId] = clobtypes.NewOffchainUpdates() | ||
} | ||
updates[clobPairId].Messages = append(updates[clobPairId].Messages, message) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't have to be now, but do we want to condense the messages somehow? We have this condensing function for indexer, but i don't think it works in this case. these events might be condensed differently.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this gets called before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It gets called during Replay Operations but i feel it might not be used half the time due to this code:
I think it should be fine, unless you can think of any edge case where condensing messages drops anything vital for orderbook updates There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah should be fine to send for example (place + remove) for example |
||
|
||
// Unmarshal messages to v1 updates. | ||
v1updates := make(map[uint32][]ocutypes.OffChainUpdateV1) | ||
for clobPairId, update := range updates { | ||
v1updates[clobPairId] = GetOffchainUpdatesV1(update) | ||
} | ||
|
||
sm.Lock() | ||
defer sm.Unlock() | ||
|
||
// Send updates to subscribers. | ||
idsToRemove := make([]uint32, 0) | ||
for id, subscription := range sm.orderbookSubscriptions { | ||
jonfung-dydx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for _, clobPairId := range subscription.clobPairIds { | ||
if updates, ok := v1updates[clobPairId]; ok { | ||
if err := subscription.srv.Send( | ||
&clobtypes.StreamOrderbookUpdatesResponse{ | ||
Updates: updates, | ||
Snapshot: false, | ||
}, | ||
); err != nil { | ||
idsToRemove = append(idsToRemove, id) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Clean up subscriptions that have been closed. | ||
jayy04 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for _, id := range idsToRemove { | ||
sm.orderbookSubscriptions[id].finished <- true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. close the channel if it's done and this is the only goroutine that sends stuff into this channel? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually I think we can just use |
||
delete(sm.orderbookSubscriptions, id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure that this delete will properly clean up the nested inner data structures? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like there's no close function 🆗 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah wasn't able to find a close function either not really sure if we need to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/grpc/grpc-go/blob/v1.60.x/stream.go#L262-L278
I'm assuming an error will be generated by SendMsg in this case so the cancel function will be called. |
||
} | ||
} | ||
|
||
// GetOffchainUpdatesV1 unmarshals messages in offchain updates to OffchainUpdateV1. | ||
func GetOffchainUpdatesV1(offchainUpdates *clobtypes.OffchainUpdates) []ocutypes.OffChainUpdateV1 { | ||
v1updates := make([]ocutypes.OffChainUpdateV1, 0) | ||
for _, message := range offchainUpdates.Messages { | ||
var update ocutypes.OffChainUpdateV1 | ||
proto.Unmarshal(message.Message.Value, &update) | ||
v1updates = append(v1updates, update) | ||
} | ||
return v1updates | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1241,6 +1241,8 @@ func (k Keeper) SendOffchainMessages( | |
} | ||
k.GetIndexerEventManager().SendOffchainData(update) | ||
} | ||
|
||
k.GetGrpcStreamingManager().SendOrderbookUpdates(offchainUpdates) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The addition of Ensure proper validation and error handling within |
||
} | ||
|
||
// getFillQuoteQuantums returns the total fillAmount price in quote quantums based on the maker subticks. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will add the if statement back in in the next PR