From 94460bcdf05c5ef0dd9953d1372f33ba48409e34 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:50:32 -0700 Subject: [PATCH 01/33] Scaffold MLS server --- dev/docker/docker-compose.yml | 7 ++++ go.mod | 2 +- go.sum | 6 +++ pkg/api/config.go | 3 ++ pkg/api/message/v3/service.go | 73 +++++++++++++++++++++++++++++++++++ pkg/api/server.go | 19 +++++++++ pkg/mlsstore/config.go | 20 ++++++++++ pkg/mlsstore/store.go | 28 ++++++++++++++ pkg/server/options.go | 16 ++++---- pkg/server/server.go | 15 +++++++ 10 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 pkg/api/message/v3/service.go create mode 100644 pkg/mlsstore/config.go create mode 100644 pkg/mlsstore/store.go diff --git a/dev/docker/docker-compose.yml b/dev/docker/docker-compose.yml index 6661f393..bb052823 100644 --- a/dev/docker/docker-compose.yml +++ b/dev/docker/docker-compose.yml @@ -11,6 +11,13 @@ services: POSTGRES_PASSWORD: xmtp ports: - 6543:5432 + mls-db: + image: postgres:13 + environment: + POSTGRES_PASSWORD: xmtp + ports: + - 7654:5432 + prometheus: image: prom/prometheus ports: diff --git a/go.mod b/go.mod index 412f191f..00328265 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/uptrace/bun/driver/pgdriver v1.1.3 github.com/waku-org/go-waku v0.8.0 github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 - github.com/xmtp/proto/v3 v3.27.0 + github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4 github.com/yoheimuta/protolint v0.39.0 go.uber.org/zap v1.24.0 golang.org/x/sync v0.3.0 diff --git a/go.sum b/go.sum index a977a834..8daa29d7 100644 --- a/go.sum +++ b/go.sum @@ -1142,6 +1142,12 @@ github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 h1:wzUffJGCTBGXIDy github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3/go.mod h1:bJREWk+NDnZYjgLQdAi8SUWuq/5pkMme4GqiffEhUF4= github.com/xmtp/proto/v3 v3.27.0 h1:G70006UEffkCmWvp9G/7Dywosj1sLm9StR5HWEb891U= github.com/xmtp/proto/v3 v3.27.0/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019020501-b49bc6ffb5eb h1:q2lR64lGFehm8m0FtcdRDMeH8MlkMyU4sz235+Ufq9E= +github.com/xmtp/proto/v3 v3.29.1-0.20231019020501-b49bc6ffb5eb/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a h1:kDEPyzhqQO9YdRAfvl21ysitvzWjdu4Ai8YCvHwqqbY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4 h1:Mxnc833msN9gX8DJyELd+E7oUJNHlhbIsZlTd88kg5M= +github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/yoheimuta/go-protoparser/v4 v4.6.0 h1:uvz1e9/5Ihsm4Ku8AJeDImTpirKmIxubZdSn0QJNdnw= github.com/yoheimuta/go-protoparser/v4 v4.6.0/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8= diff --git a/pkg/api/config.go b/pkg/api/config.go index 96cf8ea7..f61d8ff4 100644 --- a/pkg/api/config.go +++ b/pkg/api/config.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" wakunode "github.com/waku-org/go-waku/waku/v2/node" "github.com/xmtp/xmtp-node-go/pkg/authz" + "github.com/xmtp/xmtp-node-go/pkg/mlsstore" "github.com/xmtp/xmtp-node-go/pkg/ratelimiter" "github.com/xmtp/xmtp-node-go/pkg/store" "go.uber.org/zap" @@ -25,6 +26,7 @@ type Options struct { HTTPPort uint `long:"http-port" description:"API HTTP listening port" default:"5555"` Authn AuthnOptions `group:"API Authentication Options" namespace:"authn"` MaxMsgSize int `long:"max-msg-size" description:"Max message size in bytes (default 50MB)" default:"52428800"` + EnableMls bool `long:"enable-mls" description:"Enable the MLS server"` } type Config struct { @@ -33,6 +35,7 @@ type Config struct { Waku *wakunode.WakuNode Log *zap.Logger Store *store.Store + MlsStore mlsstore.MlsStore } // AuthnOptions bundle command line options associated with the authn package. diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go new file mode 100644 index 00000000..54df462b --- /dev/null +++ b/pkg/api/message/v3/service.go @@ -0,0 +1,73 @@ +package api + +import ( + "context" + + wakunode "github.com/waku-org/go-waku/waku/v2/node" + proto "github.com/xmtp/proto/v3/go/message_api/v3" + "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/store" + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +type Service struct { + proto.UnimplementedMlsApiServer + + log *zap.Logger + waku *wakunode.WakuNode + messageStore *store.Store + mlsStore mlsstore.MlsStore + + ctx context.Context + ctxCancel func() +} + +func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore) (s *Service, err error) { + s = &Service{ + log: logger.Named("message/v3"), + waku: node, + messageStore: messageStore, + mlsStore: mlsStore, + } + + s.ctx, s.ctxCancel = context.WithCancel(context.Background()) + + return s, nil +} + +func (s *Service) Close() { + if s.ctxCancel != nil { + s.ctxCancel() + } +} + +func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterInstallationRequest) (*proto.RegisterInstallationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) PublishWelcomes(ctx context.Context, req *proto.PublishWelcomesRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPackagesRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) RevokeInstallation(ctx context.Context, req *proto.RevokeInstallationRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} + +func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentityUpdatesRequest) (*proto.GetIdentityUpdatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "unimplemented") +} diff --git a/pkg/api/server.go b/pkg/api/server.go index 7c6e5990..5fe8b060 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -15,6 +15,7 @@ import ( "github.com/pkg/errors" swgui "github.com/swaggest/swgui/v3" proto "github.com/xmtp/proto/v3/go/message_api/v1" + v3Proto "github.com/xmtp/proto/v3/go/message_api/v3" messagev1openapi "github.com/xmtp/proto/v3/openapi/message_api/v1" "github.com/xmtp/xmtp-node-go/pkg/ratelimiter" "github.com/xmtp/xmtp-node-go/pkg/tracing" @@ -27,6 +28,7 @@ import ( messagev1 "github.com/xmtp/xmtp-node-go/pkg/api/message/v1" apicontext "github.com/xmtp/xmtp-node-go/pkg/api/message/v1/context" + messagev3 "github.com/xmtp/xmtp-node-go/pkg/api/message/v3" ) const ( @@ -43,6 +45,7 @@ type Server struct { grpcListener net.Listener httpListener net.Listener messagev1 *messagev1.Service + messagev3 *messagev3.Service wg sync.WaitGroup ctx context.Context @@ -125,6 +128,15 @@ func (s *Server) startGRPC() error { return errors.Wrap(err, "creating message service") } proto.RegisterMessageApiServer(grpcServer, s.messagev1) + + // Enable the MLS server if a store is provided + if s.Config.MlsStore != nil && s.Config.EnableMls { + s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store) + if err != nil { + return errors.Wrap(err, "creating mls service") + } + v3Proto.RegisterMlsApiServer(grpcServer, s.messagev3) + } prometheus.Register(grpcServer) tracing.GoPanicWrap(s.ctx, &s.wg, "grpc", func(ctx context.Context) { @@ -171,6 +183,13 @@ func (s *Server) startHTTP() error { return errors.Wrap(err, "registering message handler") } + if s.Config.MlsStore != nil && s.Config.EnableMls { + err = v3Proto.RegisterMlsApiHandler(s.ctx, gwmux, conn) + if err != nil { + return errors.Wrap(err, "registering mls handler") + } + } + addr := addrString(s.HTTPAddress, s.HTTPPort) s.httpListener, err = net.Listen("tcp", addr) if err != nil { diff --git a/pkg/mlsstore/config.go b/pkg/mlsstore/config.go new file mode 100644 index 00000000..3c84e7e0 --- /dev/null +++ b/pkg/mlsstore/config.go @@ -0,0 +1,20 @@ +package mlsstore + +import ( + "time" + + "github.com/uptrace/bun" + "go.uber.org/zap" +) + +type MlsOptions struct { + DbConnectionString string `long:"db-connection-string" description:"Connection string for MLS DB"` + ReadTimeout time.Duration `long:"read-timeout" description:"Timeout for reading from the database" default:"10s"` + WriteTimeout time.Duration `long:"write-timeout" description:"Timeout for writing to the database" default:"10s"` + MaxOpenConns int `long:"max-open-conns" description:"Maximum number of open connections" default:"80"` +} + +type Config struct { + Log *zap.Logger + DB *bun.DB +} diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go new file mode 100644 index 00000000..4dcfd277 --- /dev/null +++ b/pkg/mlsstore/store.go @@ -0,0 +1,28 @@ +package mlsstore + +import ( + "context" + + "github.com/uptrace/bun" + "go.uber.org/zap" +) + +type Store struct { + ctx context.Context + cancel context.CancelFunc + log *zap.Logger + db *bun.DB +} + +type MlsStore interface { +} + +func New(config Config) (*Store, error) { + s := &Store{ + log: config.Log.Named("mlsstore"), + db: config.DB, + } + s.ctx, s.cancel = context.WithCancel(context.Background()) + + return s, nil +} diff --git a/pkg/server/options.go b/pkg/server/options.go index e9814617..40041f4d 100644 --- a/pkg/server/options.go +++ b/pkg/server/options.go @@ -4,6 +4,7 @@ import ( "time" "github.com/xmtp/xmtp-node-go/pkg/api" + "github.com/xmtp/xmtp-node-go/pkg/mlsstore" "github.com/xmtp/xmtp-node-go/pkg/store" ) @@ -66,11 +67,12 @@ type Options struct { GoProfiling bool `long:"go-profiling" description:"Enable Go profiling"` MetricsPeriod time.Duration `long:"metrics-period" description:"Polling period for server status metrics" default:"30s"` - API api.Options `group:"API Options" namespace:"api"` - Authz AuthzOptions `group:"Authz Options"` - Relay RelayOptions `group:"Relay Options"` - Store store.Options `group:"Store Options" namespace:"store"` - Metrics MetricsOptions `group:"Metrics Options"` - Tracing TracingOptions `group:"DD APM Tracing Options"` - Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` + API api.Options `group:"API Options" namespace:"api"` + Authz AuthzOptions `group:"Authz Options"` + Relay RelayOptions `group:"Relay Options"` + Store store.Options `group:"Store Options" namespace:"store"` + Metrics MetricsOptions `group:"Metrics Options"` + Tracing TracingOptions `group:"DD APM Tracing Options"` + Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` + MlsStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mlsstore"` } diff --git a/pkg/server/server.go b/pkg/server/server.go index 3d9ed5f5..2cfbaf29 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -37,6 +37,7 @@ import ( "github.com/xmtp/xmtp-node-go/pkg/metrics" authzmigrations "github.com/xmtp/xmtp-node-go/pkg/migrations/authz" messagemigrations "github.com/xmtp/xmtp-node-go/pkg/migrations/messages" + "github.com/xmtp/xmtp-node-go/pkg/mlsstore" xmtpstore "github.com/xmtp/xmtp-node-go/pkg/store" "github.com/xmtp/xmtp-node-go/pkg/tracing" "go.uber.org/zap" @@ -225,6 +226,19 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } s.log.With(logging.MultiAddrs("listen", maddrs...)).Info("got server") + var mlsStore mlsstore.MlsStore + + if options.MlsStore.DbConnectionString != "" { + mlsDb, err := createBunDB(options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.MlsStore.MaxOpenConns) + if err != nil { + return nil, errors.Wrap(err, "creating mls db") + } + mlsStore, err = mlsstore.New(mlsstore.Config{ + Log: s.log, + DB: mlsDb, + }) + } + // Initialize gRPC server. s.grpc, err = api.New( &api.Config{ @@ -232,6 +246,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) Log: s.log.Named("api"), Waku: s.wakuNode, Store: s.store, + MlsStore: mlsStore, AllowLister: s.allowLister, }, ) From ae8023f6ed28161446c84569e02c972219ceea7a Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:52:50 -0700 Subject: [PATCH 02/33] Update go.mod --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 00328265..026e3958 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/uptrace/bun/driver/pgdriver v1.1.3 github.com/waku-org/go-waku v0.8.0 github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 - github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4 + github.com/xmtp/proto/v3 v3.30.0 github.com/yoheimuta/protolint v0.39.0 go.uber.org/zap v1.24.0 golang.org/x/sync v0.3.0 diff --git a/go.sum b/go.sum index 8daa29d7..165565fe 100644 --- a/go.sum +++ b/go.sum @@ -1148,6 +1148,8 @@ github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a h1:kDEPyzhqQO9YdR github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4 h1:Mxnc833msN9gX8DJyELd+E7oUJNHlhbIsZlTd88kg5M= github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.30.0 h1:x6LGCWpO2HTQNhUiTXfE0l+u2HSL3Z35p41xhgy6hlw= +github.com/xmtp/proto/v3 v3.30.0/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/yoheimuta/go-protoparser/v4 v4.6.0 h1:uvz1e9/5Ihsm4Ku8AJeDImTpirKmIxubZdSn0QJNdnw= github.com/yoheimuta/go-protoparser/v4 v4.6.0/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8= From 0fccac95b12aec7a163b799de7454c8cfce091b8 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:08:16 -0700 Subject: [PATCH 03/33] Fix missing argument --- pkg/api/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/server.go b/pkg/api/server.go index 5fe8b060..e7f1b643 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -131,7 +131,7 @@ func (s *Server) startGRPC() error { // Enable the MLS server if a store is provided if s.Config.MlsStore != nil && s.Config.EnableMls { - s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store) + s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MlsStore) if err != nil { return errors.Wrap(err, "creating mls service") } From 9f987950097c3d107fbb9623c14b7059d7413d25 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:09:20 -0700 Subject: [PATCH 04/33] Add unsaved file --- pkg/mlsstore/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mlsstore/config.go b/pkg/mlsstore/config.go index 3c84e7e0..521ec282 100644 --- a/pkg/mlsstore/config.go +++ b/pkg/mlsstore/config.go @@ -7,7 +7,7 @@ import ( "go.uber.org/zap" ) -type MlsOptions struct { +type StoreOptions struct { DbConnectionString string `long:"db-connection-string" description:"Connection string for MLS DB"` ReadTimeout time.Duration `long:"read-timeout" description:"Timeout for reading from the database" default:"10s"` WriteTimeout time.Duration `long:"write-timeout" description:"Timeout for writing to the database" default:"10s"` From d4962714d00dd93271e11c18ea2b19d2de5855da Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:11:17 -0700 Subject: [PATCH 05/33] Lint --- pkg/server/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/server/server.go b/pkg/server/server.go index 2cfbaf29..adf2ca53 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -233,10 +233,14 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) if err != nil { return nil, errors.Wrap(err, "creating mls db") } + mlsStore, err = mlsstore.New(mlsstore.Config{ Log: s.log, DB: mlsDb, }) + if err != nil { + return nil, errors.Wrap(err, "creating mls store") + } } // Initialize gRPC server. From 0eda2548de1bfeff713097f83b8bc3fd5720de44 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:52:24 -0700 Subject: [PATCH 06/33] Working end-to-end --- cmd/xmtpd/main.go | 7 + go.mod | 19 +- go.sum | 58 +++--- pkg/api/config.go | 12 +- pkg/api/message/v3/service.go | 96 ++++++++-- pkg/api/server.go | 4 +- .../mls/20231023050806_init-schema.down.sql | 8 + .../mls/20231023050806_init-schema.up.sql | 44 +++++ pkg/migrations/mls/migrations.go | 18 ++ pkg/mlsstore/models.go | 23 +++ pkg/mlsstore/store.go | 155 +++++++++++++++- pkg/mlsstore/store_test.go | 171 ++++++++++++++++++ pkg/mlsvalidate/config.go | 5 + pkg/mlsvalidate/service.go | 108 +++++++++++ pkg/mlsvalidate/service_test.go | 63 +++++++ pkg/server/options.go | 19 +- pkg/server/server.go | 45 ++++- pkg/testing/store.go | 15 ++ 18 files changed, 797 insertions(+), 73 deletions(-) create mode 100644 pkg/migrations/mls/20231023050806_init-schema.down.sql create mode 100644 pkg/migrations/mls/20231023050806_init-schema.up.sql create mode 100644 pkg/migrations/mls/migrations.go create mode 100644 pkg/mlsstore/models.go create mode 100644 pkg/mlsstore/store_test.go create mode 100644 pkg/mlsvalidate/config.go create mode 100644 pkg/mlsvalidate/service.go create mode 100644 pkg/mlsvalidate/service_test.go diff --git a/cmd/xmtpd/main.go b/cmd/xmtpd/main.go index cd948fff..69815c1b 100644 --- a/cmd/xmtpd/main.go +++ b/cmd/xmtpd/main.go @@ -92,6 +92,13 @@ func main() { return } + if options.CreateMlsMigration != "" && options.MlsStore.DbConnectionString != "" { + if err := server.CreateMlsMigration(options.CreateMlsMigration, options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.Store.MaxOpenConns); err != nil { + log.Fatal("creating authz db migration", zap.Error(err)) + } + return + } + if options.Tracing.Enable { log.Info("starting tracer") tracing.Start(Commit, utils.Logger()) diff --git a/go.mod b/go.mod index 026e3958..e86b30f8 100644 --- a/go.mod +++ b/go.mod @@ -25,12 +25,12 @@ require ( github.com/prometheus/client_golang v1.14.0 github.com/stretchr/testify v1.8.4 github.com/swaggest/swgui v1.6.2 - github.com/uptrace/bun v1.1.3 - github.com/uptrace/bun/dialect/pgdialect v1.1.3 - github.com/uptrace/bun/driver/pgdriver v1.1.3 + github.com/uptrace/bun v1.1.16 + github.com/uptrace/bun/dialect/pgdialect v1.1.16 + github.com/uptrace/bun/driver/pgdriver v1.1.16 github.com/waku-org/go-waku v0.8.0 github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 - github.com/xmtp/proto/v3 v3.30.0 + github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4 github.com/yoheimuta/protolint v0.39.0 go.uber.org/zap v1.24.0 golang.org/x/sync v0.3.0 @@ -108,7 +108,7 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.55 // indirect @@ -148,6 +148,7 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tinylib/msgp v1.1.2 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect @@ -167,12 +168,12 @@ require ( go.uber.org/dig v1.17.0 // indirect go.uber.org/fx v1.20.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.14.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect @@ -181,7 +182,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect - mellium.im/sasl v0.2.1 // indirect + mellium.im/sasl v0.3.1 // indirect ) // From node-go diff --git a/go.sum b/go.sum index 165565fe..506a1663 100644 --- a/go.sum +++ b/go.sum @@ -94,6 +94,7 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -298,6 +299,7 @@ github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= @@ -633,16 +635,19 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= @@ -733,8 +738,9 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -747,6 +753,7 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -803,6 +810,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -834,6 +842,7 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= @@ -965,6 +974,7 @@ github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1 github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -1037,7 +1047,9 @@ github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZL github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1046,7 +1058,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggest/swgui v1.6.2 h1:DR+ioYt11YrXMaEmLcgaOEFSZ/8QW30uYYE/Ck41cPA= @@ -1086,12 +1100,12 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/uptrace/bun v1.1.3 h1:v62tsUyKjVCR5q7J49uckM6CVVTqMO26aV73F3G6RFk= -github.com/uptrace/bun v1.1.3/go.mod h1:aQbKvxs7/n9MMef/b8lYOh5Rwlo4Jd5A31E4HlYNqSc= -github.com/uptrace/bun/dialect/pgdialect v1.1.3 h1:EMRCC98YKSpo/EXyujsr+5v0PKYkRE0rwxJKKEcrOuE= -github.com/uptrace/bun/dialect/pgdialect v1.1.3/go.mod h1:2GJogfkVHmCKxt6N88vRbJNSUV5wfPym/rp6N25dShc= -github.com/uptrace/bun/driver/pgdriver v1.1.3 h1:WWxEfGnJQCXgODtjU37E+XWEVvCGwvs2fRgCYFqmKAY= -github.com/uptrace/bun/driver/pgdriver v1.1.3/go.mod h1:D7tTNXLIR9udcf/Dm9W+x1qvY+GDCkYVIRLgQyMElCY= +github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A= +github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8= +github.com/uptrace/bun/dialect/pgdialect v1.1.16 h1:eUPZ+YCJ69BA+W1X1ZmpOJSkv1oYtinr0zCXf7zCo5g= +github.com/uptrace/bun/dialect/pgdialect v1.1.16/go.mod h1:KQjfx/r6JM0OXfbv0rFrxAbdkPD7idK8VitnjIV9fZI= +github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7QiNOCPLqPFJjK4= +github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= @@ -1140,16 +1154,10 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 h1:wzUffJGCTBGXIDyNU+1UBu1fn2Nzo+OQzM1pLrheh58= github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3/go.mod h1:bJREWk+NDnZYjgLQdAi8SUWuq/5pkMme4GqiffEhUF4= -github.com/xmtp/proto/v3 v3.27.0 h1:G70006UEffkCmWvp9G/7Dywosj1sLm9StR5HWEb891U= -github.com/xmtp/proto/v3 v3.27.0/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= -github.com/xmtp/proto/v3 v3.29.1-0.20231019020501-b49bc6ffb5eb h1:q2lR64lGFehm8m0FtcdRDMeH8MlkMyU4sz235+Ufq9E= -github.com/xmtp/proto/v3 v3.29.1-0.20231019020501-b49bc6ffb5eb/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= -github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a h1:kDEPyzhqQO9YdRAfvl21ysitvzWjdu4Ai8YCvHwqqbY= -github.com/xmtp/proto/v3 v3.29.1-0.20231019022514-1cc4b0d5a51a/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= -github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4 h1:Mxnc833msN9gX8DJyELd+E7oUJNHlhbIsZlTd88kg5M= -github.com/xmtp/proto/v3 v3.29.1-0.20231019163152-2a17d00f45f4/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= -github.com/xmtp/proto/v3 v3.30.0 h1:x6LGCWpO2HTQNhUiTXfE0l+u2HSL3Z35p41xhgy6hlw= -github.com/xmtp/proto/v3 v3.30.0/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019225839-328520e94f34 h1:rR10cJ5RTlw7OWdw5fnNF3WpByRybvGC3xmOmELd7JY= +github.com/xmtp/proto/v3 v3.29.1-0.20231019225839-328520e94f34/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4 h1:Qc2ed8NrlosJnPMNxVriugcFB21d4V90HKZdO83yV2M= +github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/yoheimuta/go-protoparser/v4 v4.6.0 h1:uvz1e9/5Ihsm4Ku8AJeDImTpirKmIxubZdSn0QJNdnw= github.com/yoheimuta/go-protoparser/v4 v4.6.0/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8= @@ -1239,8 +1247,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1439,10 +1447,11 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1456,8 +1465,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1705,8 +1714,9 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= +mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/pkg/api/config.go b/pkg/api/config.go index f61d8ff4..be09a094 100644 --- a/pkg/api/config.go +++ b/pkg/api/config.go @@ -8,6 +8,7 @@ import ( wakunode "github.com/waku-org/go-waku/waku/v2/node" "github.com/xmtp/xmtp-node-go/pkg/authz" "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" "github.com/xmtp/xmtp-node-go/pkg/ratelimiter" "github.com/xmtp/xmtp-node-go/pkg/store" "go.uber.org/zap" @@ -31,11 +32,12 @@ type Options struct { type Config struct { Options - AllowLister authz.WalletAllowLister - Waku *wakunode.WakuNode - Log *zap.Logger - Store *store.Store - MlsStore mlsstore.MlsStore + AllowLister authz.WalletAllowLister + Waku *wakunode.WakuNode + Log *zap.Logger + Store *store.Store + MlsStore mlsstore.MlsStore + MlsValidator mlsvalidate.MlsValidationService } // AuthnOptions bundle command line options associated with the authn package. diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 54df462b..662f1a18 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -6,6 +6,7 @@ import ( wakunode "github.com/waku-org/go-waku/waku/v2/node" proto "github.com/xmtp/proto/v3/go/message_api/v3" "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" "github.com/xmtp/xmtp-node-go/pkg/store" "go.uber.org/zap" "google.golang.org/grpc/codes" @@ -16,21 +17,23 @@ import ( type Service struct { proto.UnimplementedMlsApiServer - log *zap.Logger - waku *wakunode.WakuNode - messageStore *store.Store - mlsStore mlsstore.MlsStore + log *zap.Logger + waku *wakunode.WakuNode + messageStore *store.Store + mlsStore mlsstore.MlsStore + validationService mlsvalidate.MlsValidationService ctx context.Context ctxCancel func() } -func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore) (s *Service, err error) { +func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore, validationService mlsvalidate.MlsValidationService) (s *Service, err error) { s = &Service{ - log: logger.Named("message/v3"), - waku: node, - messageStore: messageStore, - mlsStore: mlsStore, + log: logger.Named("message/v3"), + waku: node, + messageStore: messageStore, + mlsStore: mlsStore, + validationService: validationService, } s.ctx, s.ctxCancel = context.WithCancel(context.Background()) @@ -45,11 +48,50 @@ func (s *Service) Close() { } func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterInstallationRequest) (*proto.RegisterInstallationResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") + results, err := s.validationService.ValidateKeyPackages(ctx, [][]byte{req.LastResortKeyPackage.KeyPackageTlsSerialized}) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid identity: %s", err) + } + if len(results) != 1 { + return nil, status.Errorf(codes.Internal, "unexpected number of results: %d", len(results)) + } + + installationId := results[0].InstallationId + walletAddress := results[0].WalletAddress + + err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) + if err != nil { + return nil, err + } + + return &proto.RegisterInstallationResponse{ + InstallationId: installationId, + }, nil } func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") + ids := req.InstallationIds + keyPackages, err := s.mlsStore.ConsumeKeyPackages(ctx, ids) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) + } + + resPackages := make([]*proto.ConsumeKeyPackagesResponse_KeyPackage, len(keyPackages)) + for _, keyPackage := range keyPackages { + // Return the key packages in the original order + targetIndex := indexOf(keyPackage.InstallationId, ids) + if targetIndex == -1 { + return nil, status.Errorf(codes.Internal, "could not find key package for installation") + } + + resPackages[targetIndex] = &proto.ConsumeKeyPackagesResponse_KeyPackage{ + KeyPackageTlsSerialized: keyPackage.Data, + } + } + + return &proto.ConsumeKeyPackagesResponse{ + KeyPackages: resPackages, + }, nil } func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupRequest) (*emptypb.Empty, error) { @@ -61,7 +103,27 @@ func (s *Service) PublishWelcomes(ctx context.Context, req *proto.PublishWelcome } func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPackagesRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") + keyPackageBytes := make([][]byte, len(req.KeyPackages)) + for i, keyPackage := range req.KeyPackages { + keyPackageBytes[i] = keyPackage.KeyPackageTlsSerialized + } + validationResults, err := s.validationService.ValidateKeyPackages(ctx, keyPackageBytes) + if err != nil { + // TODO: Differentiate between validation errors and internal errors + return nil, status.Errorf(codes.InvalidArgument, "invalid identity: %s", err) + } + + keyPackageModels := make([]*mlsstore.KeyPackage, len(validationResults)) + for i, validationResult := range validationResults { + kp := mlsstore.NewKeyPackage(validationResult.InstallationId, keyPackageBytes[i], false) + keyPackageModels[i] = &kp + } + err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to insert key packages: %s", err) + } + + return &emptypb.Empty{}, nil } func (s *Service) RevokeInstallation(ctx context.Context, req *proto.RevokeInstallationRequest) (*emptypb.Empty, error) { @@ -71,3 +133,13 @@ func (s *Service) RevokeInstallation(ctx context.Context, req *proto.RevokeInsta func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentityUpdatesRequest) (*proto.GetIdentityUpdatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "unimplemented") } + +func indexOf(target string, ids []string) int { + for i, id := range ids { + if id == target { + return i + } + } + + return -1 +} diff --git a/pkg/api/server.go b/pkg/api/server.go index e7f1b643..8af7c951 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -130,8 +130,8 @@ func (s *Server) startGRPC() error { proto.RegisterMessageApiServer(grpcServer, s.messagev1) // Enable the MLS server if a store is provided - if s.Config.MlsStore != nil && s.Config.EnableMls { - s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MlsStore) + if s.Config.MlsStore != nil && s.Config.MlsValidator != nil && s.Config.EnableMls { + s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MlsStore, s.Config.MlsValidator) if err != nil { return errors.Wrap(err, "creating mls service") } diff --git a/pkg/migrations/mls/20231023050806_init-schema.down.sql b/pkg/migrations/mls/20231023050806_init-schema.down.sql new file mode 100644 index 00000000..b1077270 --- /dev/null +++ b/pkg/migrations/mls/20231023050806_init-schema.down.sql @@ -0,0 +1,8 @@ +SET + statement_timeout = 0; + +--bun:split +DROP TABLE IF EXISTS installations; + +--bun:split +DROP TABLE IF EXISTS key_packages; \ No newline at end of file diff --git a/pkg/migrations/mls/20231023050806_init-schema.up.sql b/pkg/migrations/mls/20231023050806_init-schema.up.sql new file mode 100644 index 00000000..193d5474 --- /dev/null +++ b/pkg/migrations/mls/20231023050806_init-schema.up.sql @@ -0,0 +1,44 @@ +SET + statement_timeout = 0; + +--bun:split +CREATE TABLE installations ( + id TEXT PRIMARY KEY, + wallet_address TEXT NOT NULL, + created_at BIGINT NOT NULL, + revoked_at BIGINT +); + +--bun:split +CREATE TABLE key_packages ( + id TEXT PRIMARY KEY, + installation_id TEXT NOT NULL, + created_at BIGINT NOT NULL, + consumed_at BIGINT, + is_last_resort BOOLEAN NOT NULL, + data BYTEA NOT NULL, + -- Add a foreign key constraint to ensure key packages cannot be added for unregistered installations + CONSTRAINT fk_installation_id FOREIGN KEY (installation_id) REFERENCES installations (id) +); + +--bun:split +CREATE INDEX idx_installations_wallet_address ON installations(wallet_address); + +--bun:split +CREATE INDEX idx_installations_created_at ON installations(created_at); + +--bun:split +CREATE INDEX idx_installations_revoked_at ON installations(revoked_at); + +--bun:split +-- Adding indexes for the key_packages table +CREATE INDEX idx_key_packages_installation_id ON key_packages(installation_id); + +--bun:split +CREATE INDEX idx_key_packages_created_at ON key_packages(created_at); + +--bun:split +CREATE INDEX idx_key_packages_consumed_at ON key_packages(consumed_at); + +--bun:split +CREATE INDEX idx_key_packages_is_last_resort ON key_packages(is_last_resort); \ No newline at end of file diff --git a/pkg/migrations/mls/migrations.go b/pkg/migrations/mls/migrations.go new file mode 100644 index 00000000..860e0445 --- /dev/null +++ b/pkg/migrations/mls/migrations.go @@ -0,0 +1,18 @@ +package mls + +import ( + "embed" + + "github.com/uptrace/bun/migrate" +) + +var Migrations = migrate.NewMigrations() + +//go:embed *.sql +var sqlMigrations embed.FS + +func init() { + if err := Migrations.Discover(sqlMigrations); err != nil { + panic(err) + } +} diff --git a/pkg/mlsstore/models.go b/pkg/mlsstore/models.go new file mode 100644 index 00000000..4df764bc --- /dev/null +++ b/pkg/mlsstore/models.go @@ -0,0 +1,23 @@ +package mlsstore + +import "github.com/uptrace/bun" + +type Installation struct { + bun.BaseModel `bun:"table:installations"` + + ID string `bun:",pk"` + WalletAddress string `bun:"wallet_address,notnull"` + CreatedAt int64 `bun:"created_at,notnull"` + RevokedAt *int64 `bun:"revoked_at"` +} + +type KeyPackage struct { + bun.BaseModel `bun:"table:key_packages"` + + ID string `bun:",pk"` // ID is the hash of the data field + InstallationId string `bun:"installation_id,notnull"` + CreatedAt int64 `bun:"created_at,notnull"` + ConsumedAt *int64 `bun:"consumed_at"` + IsLastResort bool `bun:"is_last_resort,notnull"` + Data []byte `bun:"data,notnull,type:bytea"` +} diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 4dcfd277..bfc91701 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -2,27 +2,170 @@ package mlsstore import ( "context" + "crypto/sha256" + "database/sql" + "encoding/hex" + "errors" + "time" "github.com/uptrace/bun" + "github.com/uptrace/bun/migrate" + "github.com/xmtp/xmtp-node-go/pkg/migrations/messages" "go.uber.org/zap" ) type Store struct { - ctx context.Context - cancel context.CancelFunc + config Config log *zap.Logger db *bun.DB } type MlsStore interface { + CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error + InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage) error + ConsumeKeyPackages(ctx context.Context, installationIds []string) ([]*KeyPackage, error) } -func New(config Config) (*Store, error) { +func New(ctx context.Context, config Config) (*Store, error) { s := &Store{ - log: config.Log.Named("mlsstore"), - db: config.DB, + log: config.Log.Named("mlsstore"), + db: config.DB, + config: config, + } + + if err := s.migrate(ctx); err != nil { + return nil, err } - s.ctx, s.cancel = context.WithCancel(context.Background()) return s, nil } + +func (s *Store) Close() { + if s.db != nil { + s.db.Close() + } +} + +// Creates the installation and last resort key package +func (s *Store) CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error { + createdAt := nowNs() + + installation := Installation{ + ID: installationId, + WalletAddress: walletAddress, + CreatedAt: createdAt, + } + + keyPackage := NewKeyPackage(installationId, lastResortKeyPackage, true) + + return s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + _, err := tx.NewInsert(). + Model(&installation). + Ignore(). + Exec(ctx) + + if err != nil { + return err + } + + _, err = tx.NewInsert(). + Model(&keyPackage). + Ignore(). + Exec(ctx) + + if err != nil { + return err + } + + return nil + }) +} + +// Insert a batch of key packages, ignoring any that may already exist +func (s *Store) InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage) error { + _, err := s.db.NewInsert().Model(&keyPackages).Ignore().Exec(ctx) + return err +} + +func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string) ([]*KeyPackage, error) { + keyPackages := make([]*KeyPackage, 0) + err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + err := tx.NewRaw(` + SELECT DISTINCT ON(installation_id) * FROM key_packages + WHERE "installation_id" IN (?) + AND "consumed_at" IS NULL + ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC + `, + bun.In(installationIds)). + Scan(ctx, &keyPackages) + + if err != nil { + return err + } + + if len(keyPackages) < len(installationIds) { + return errors.New("key packages not found") + } + + _, err = tx.NewUpdate(). + Table("key_packages"). + Set("consumed_at = ?", nowNs()). + Where("is_last_resort = FALSE"). + Where("id IN (?)", bun.In(extractIds(keyPackages))). + Exec(ctx) + + return err + }) + + if err != nil { + return nil, err + } + + return keyPackages, nil +} + +func NewKeyPackage(installationId string, data []byte, isLastResort bool) KeyPackage { + return KeyPackage{ + ID: buildKeyPackageId(data), + InstallationId: installationId, + CreatedAt: nowNs(), + IsLastResort: isLastResort, + Data: data, + } +} + +func extractIds(keyPackages []*KeyPackage) []string { + out := make([]string, len(keyPackages)) + for i, keyPackage := range keyPackages { + out[i] = keyPackage.ID + } + return out +} + +func (s *Store) migrate(ctx context.Context) error { + migrator := migrate.NewMigrator(s.db, messages.Migrations) + err := migrator.Init(ctx) + if err != nil { + return err + } + + group, err := migrator.Migrate(ctx) + if err != nil { + return err + } + + if group.IsZero() { + s.log.Info("No new migrations to run") + } + + return nil +} + +func nowNs() int64 { + return time.Now().UTC().UnixNano() +} + +func buildKeyPackageId(keyPackageData []byte) string { + digest := sha256.Sum256(keyPackageData) + return hex.EncodeToString(digest[:]) +} diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go new file mode 100644 index 00000000..253b61ef --- /dev/null +++ b/pkg/mlsstore/store_test.go @@ -0,0 +1,171 @@ +package mlsstore + +import ( + "context" + "crypto/rand" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + test "github.com/xmtp/xmtp-node-go/pkg/testing" +) + +func NewTestStore(t *testing.T) (*Store, func()) { + log := test.NewLog(t) + db, _, dbCleanup := test.NewMlsDB(t) + ctx := context.Background() + c := Config{ + Log: log, + DB: db, + } + + store, err := New(ctx, c) + require.NoError(t, err) + + return store, dbCleanup +} + +func randomBytes(n int) []byte { + b := make([]byte, n) + _, _ = rand.Reader.Read(b) + return b +} + +func randomString(n int) string { + return fmt.Sprintf("%x", randomBytes(n)) +} + +func TestCreateInstallation(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + installationId := randomString(32) + walletAddress := randomString(32) + + err := store.CreateInstallation(ctx, installationId, walletAddress, randomBytes(32)) + require.NoError(t, err) + + installationFromDb := &Installation{} + require.NoError(t, store.db.NewSelect().Model(installationFromDb).Where("id = ?", installationId).Scan(ctx)) + require.Equal(t, walletAddress, installationFromDb.WalletAddress) + + keyPackageFromDb := &KeyPackage{} + require.NoError(t, store.db.NewSelect().Model(keyPackageFromDb).Where("installation_id = ?", installationId).Scan(ctx)) + require.Equal(t, installationId, keyPackageFromDb.InstallationId) +} + +func TestCreateInstallationIdempotent(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + installationId := randomString(32) + walletAddress := randomString(32) + keyPackage := randomBytes(32) + + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + require.NoError(t, err) + err = store.CreateInstallation(ctx, installationId, walletAddress, randomBytes(32)) + require.NoError(t, err) + + keyPackageFromDb := &KeyPackage{} + require.NoError(t, store.db.NewSelect().Model(keyPackageFromDb).Where("installation_id = ?", installationId).Scan(ctx)) + require.Equal(t, keyPackage, keyPackageFromDb.Data) +} + +func TestInsertKeyPackages(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + installationId := randomString(32) + walletAddress := randomString(32) + keyPackage := randomBytes(32) + + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + require.NoError(t, err) + + keyPackage2 := randomBytes(32) + err = store.InsertKeyPackages(ctx, []*KeyPackage{{ + ID: buildKeyPackageId(keyPackage2), + InstallationId: installationId, + CreatedAt: nowNs(), + IsLastResort: false, + Data: keyPackage2, + }}) + require.NoError(t, err) + + keyPackagesFromDb := []*KeyPackage{} + store.db.NewSelect().Model(&keyPackagesFromDb).Where("installation_id = ?", installationId).Scan(ctx) + require.Len(t, keyPackagesFromDb, 2) + + hasLastResort := false + hasRegular := false + for _, keyPackageFromDb := range keyPackagesFromDb { + require.Equal(t, installationId, keyPackageFromDb.InstallationId) + if keyPackageFromDb.IsLastResort { + hasLastResort = true + } + if !keyPackageFromDb.IsLastResort { + hasRegular = true + require.Equal(t, keyPackage2, keyPackageFromDb.Data) + } + } + + require.True(t, hasLastResort) + require.True(t, hasRegular) +} + +func TestConsumeLastResortKeyPackage(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + installationId := randomString(32) + walletAddress := randomString(32) + keyPackage := randomBytes(32) + + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + require.NoError(t, err) + + consumeResult, err := store.ConsumeKeyPackages(ctx, []string{installationId}) + require.NoError(t, err) + require.Len(t, consumeResult, 1) + require.Equal(t, keyPackage, consumeResult[0].Data) + require.Equal(t, installationId, consumeResult[0].InstallationId) +} + +func TestConsumeMultipleKeyPackages(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + installationId := randomString(32) + walletAddress := randomString(32) + keyPackage := randomBytes(32) + + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + require.NoError(t, err) + + keyPackage2 := randomBytes(32) + require.NoError(t, store.InsertKeyPackages(ctx, []*KeyPackage{{ + ID: buildKeyPackageId(keyPackage2), + InstallationId: installationId, + CreatedAt: nowNs(), + IsLastResort: false, + Data: keyPackage2, + }})) + + consumeResult, err := store.ConsumeKeyPackages(ctx, []string{installationId}) + require.NoError(t, err) + require.Len(t, consumeResult, 1) + require.Equal(t, keyPackage2, consumeResult[0].Data) + require.Equal(t, installationId, consumeResult[0].InstallationId) + + consumeResult, err = store.ConsumeKeyPackages(ctx, []string{installationId}) + require.NoError(t, err) + require.Len(t, consumeResult, 1) + // Now we are out of regular key packages. Expect to consume the last resort + require.Equal(t, keyPackage, consumeResult[0].Data) +} diff --git a/pkg/mlsvalidate/config.go b/pkg/mlsvalidate/config.go new file mode 100644 index 00000000..895de47f --- /dev/null +++ b/pkg/mlsvalidate/config.go @@ -0,0 +1,5 @@ +package mlsvalidate + +type MlsValidationOptions struct { + GrpcAddress string `long:"grpc-address" description:"Address for the GRPC validation service"` +} diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go new file mode 100644 index 00000000..2811e347 --- /dev/null +++ b/pkg/mlsvalidate/service.go @@ -0,0 +1,108 @@ +package mlsvalidate + +import ( + "context" + "errors" + "fmt" + + svc "github.com/xmtp/proto/v3/go/mls_validation/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type IdentityValidationResult struct { + WalletAddress string + InstallationId string +} + +type GroupMessageValidationResult struct { + GroupId string +} + +type IdentityInput struct { + SigningPublicKey []byte + Identity []byte +} + +type MlsValidationService interface { + ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]IdentityValidationResult, error) + ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]GroupMessageValidationResult, error) +} + +type MlsValidationServiceImpl struct { + grpcClient svc.ValidationApiClient +} + +func NewMlsValidationService(ctx context.Context, options MlsValidationOptions) (*MlsValidationServiceImpl, error) { + conn, err := grpc.DialContext(ctx, options.GrpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + return &MlsValidationServiceImpl{ + grpcClient: svc.NewValidationApiClient(conn), + }, nil +} + +func (s *MlsValidationServiceImpl) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]IdentityValidationResult, error) { + req := makeValidateKeyPackageRequest(keyPackages) + response, err := s.grpcClient.ValidateKeyPackages(ctx, req) + if err != nil { + return nil, err + } + out := make([]IdentityValidationResult, len(response.Responses)) + for i, response := range response.Responses { + if !response.IsOk { + return nil, errors.New(fmt.Sprintf("validation failed with error %s", response.ErrorMessage)) + } + out[i] = IdentityValidationResult{ + WalletAddress: response.WalletAddress, + InstallationId: response.InstallationId, + } + } + return out, nil +} + +func makeValidateKeyPackageRequest(keyPackageBytes [][]byte) *svc.ValidateKeyPackagesRequest { + keyPackageRequests := make([]*svc.ValidateKeyPackagesRequest_KeyPackage, len(keyPackageBytes)) + for i, keyPackage := range keyPackageBytes { + keyPackageRequests[i] = &svc.ValidateKeyPackagesRequest_KeyPackage{ + KeyPackageBytesTlsSerialized: keyPackage, + } + } + return &svc.ValidateKeyPackagesRequest{ + KeyPackages: keyPackageRequests, + } +} + +func (s *MlsValidationServiceImpl) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]GroupMessageValidationResult, error) { + req := makeValidateGroupMessagesRequest(groupMessages) + + response, err := s.grpcClient.ValidateGroupMessages(ctx, req) + if err != nil { + return nil, err + } + + out := make([]GroupMessageValidationResult, len(response.Responses)) + for i, response := range response.Responses { + if !response.IsOk { + return nil, errors.New(fmt.Sprintf("validation failed with error %s", response.ErrorMessage)) + } + out[i] = GroupMessageValidationResult{ + GroupId: response.GroupId, + } + } + + return out, nil +} + +func makeValidateGroupMessagesRequest(groupMessages [][]byte) *svc.ValidateGroupMessagesRequest { + groupMessageRequests := make([]*svc.ValidateGroupMessagesRequest_GroupMessage, len(groupMessages)) + for i, groupMessage := range groupMessages { + groupMessageRequests[i] = &svc.ValidateGroupMessagesRequest_GroupMessage{ + GroupMessageBytesTlsSerialized: groupMessage, + } + } + return &svc.ValidateGroupMessagesRequest{ + GroupMessages: groupMessageRequests, + } +} diff --git a/pkg/mlsvalidate/service_test.go b/pkg/mlsvalidate/service_test.go new file mode 100644 index 00000000..d78b3367 --- /dev/null +++ b/pkg/mlsvalidate/service_test.go @@ -0,0 +1,63 @@ +package mlsvalidate + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + svc "github.com/xmtp/proto/v3/go/mls_validation/v1" + "google.golang.org/grpc" +) + +type MockedGrpcService struct { + mock.Mock +} + +func (m *MockedGrpcService) ValidateKeyPackages(ctx context.Context, req *svc.ValidateKeyPackagesRequest, opts ...grpc.CallOption) (*svc.ValidateKeyPackagesResponse, error) { + args := m.Called(ctx, req) + + return args.Get(0).(*svc.ValidateKeyPackagesResponse), args.Error(1) +} + +func (m *MockedGrpcService) ValidateGroupMessages(ctx context.Context, req *svc.ValidateGroupMessagesRequest, opts ...grpc.CallOption) (*svc.ValidateGroupMessagesResponse, error) { + args := m.Called(ctx, req) + + return args.Get(0).(*svc.ValidateGroupMessagesResponse), args.Error(1) +} + +func getMockedService() (*MockedGrpcService, MlsValidationService) { + mockService := new(MockedGrpcService) + service := &MlsValidationServiceImpl{ + grpcClient: mockService, + } + + return mockService, service +} + +func TestValidateKeyPackages(t *testing.T) { + mockGrpc, service := getMockedService() + + ctx := context.Background() + + firstResponse := svc.ValidateKeyPackagesResponse_ValidationResponse{ + IsOk: true, + WalletAddress: "0x123", + InstallationId: "123", + ErrorMessage: "", + } + + mockGrpc.On("ValidateKeyPackages", ctx, mock.Anything).Return(&svc.ValidateKeyPackagesResponse{ + Responses: []*svc.ValidateKeyPackagesResponse_ValidationResponse{&firstResponse}, + }, nil) + + res, err := service.ValidateKeyPackages(ctx, nil) + assert.NoError(t, err) + assert.Equal(t, 1, len(res)) + assert.Equal(t, "0x123", res[0].WalletAddress) + assert.Equal(t, "123", res[0].InstallationId) +} + +func TestValidateKeyPackagesError(t *testing.T) { + +} diff --git a/pkg/server/options.go b/pkg/server/options.go index 40041f4d..aabfffba 100644 --- a/pkg/server/options.go +++ b/pkg/server/options.go @@ -5,6 +5,7 @@ import ( "github.com/xmtp/xmtp-node-go/pkg/api" "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" "github.com/xmtp/xmtp-node-go/pkg/store" ) @@ -62,17 +63,19 @@ type Options struct { LogEncoding string `long:"log-encoding" description:"Log encoding format. Either console or json" choice:"console" choice:"json" default:"console"` CreateMessageMigration string `long:"create-message-migration" default:"" description:"Create a migration. Must provide a name"` CreateAuthzMigration string `long:"create-authz-migration" default:"" description:"Create a migration for the auth db. Must provide a name"` + CreateMlsMigration string `long:"create-mls-migration" default:"" description:"Create a migration for the mls db. Must provide a name"` WaitForDB time.Duration `long:"wait-for-db" description:"wait for DB on start, up to specified duration"` Version bool `long:"version" description:"Output binary version and exit"` GoProfiling bool `long:"go-profiling" description:"Enable Go profiling"` MetricsPeriod time.Duration `long:"metrics-period" description:"Polling period for server status metrics" default:"30s"` - API api.Options `group:"API Options" namespace:"api"` - Authz AuthzOptions `group:"Authz Options"` - Relay RelayOptions `group:"Relay Options"` - Store store.Options `group:"Store Options" namespace:"store"` - Metrics MetricsOptions `group:"Metrics Options"` - Tracing TracingOptions `group:"DD APM Tracing Options"` - Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` - MlsStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mlsstore"` + API api.Options `group:"API Options" namespace:"api"` + Authz AuthzOptions `group:"Authz Options"` + Relay RelayOptions `group:"Relay Options"` + Store store.Options `group:"Store Options" namespace:"store"` + Metrics MetricsOptions `group:"Metrics Options"` + Tracing TracingOptions `group:"DD APM Tracing Options"` + Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` + MlsStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mls-store"` + MlsValidation mlsvalidate.MlsValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` } diff --git a/pkg/server/server.go b/pkg/server/server.go index adf2ca53..75747414 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -37,7 +37,9 @@ import ( "github.com/xmtp/xmtp-node-go/pkg/metrics" authzmigrations "github.com/xmtp/xmtp-node-go/pkg/migrations/authz" messagemigrations "github.com/xmtp/xmtp-node-go/pkg/migrations/messages" + mlsmigrations "github.com/xmtp/xmtp-node-go/pkg/migrations/mls" "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" xmtpstore "github.com/xmtp/xmtp-node-go/pkg/store" "github.com/xmtp/xmtp-node-go/pkg/tracing" "go.uber.org/zap" @@ -59,6 +61,7 @@ type Server struct { allowLister authz.WalletAllowLister authenticator *authn.XmtpAuthentication grpc *api.Server + mlsStore *mlsstore.Store } // Create a new Server @@ -234,24 +237,35 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) return nil, errors.Wrap(err, "creating mls db") } - mlsStore, err = mlsstore.New(mlsstore.Config{ + s.mlsStore, err = mlsstore.New(s.ctx, mlsstore.Config{ Log: s.log, DB: mlsDb, }) + if err != nil { return nil, errors.Wrap(err, "creating mls store") } } + var mlsValidator mlsvalidate.MlsValidationService + if options.MlsValidation.GrpcAddress != "" { + mlsValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MlsValidation) + if err != nil { + return nil, errors.Wrap(err, "creating mls validation service") + } + + } + // Initialize gRPC server. s.grpc, err = api.New( &api.Config{ - Options: options.API, - Log: s.log.Named("api"), - Waku: s.wakuNode, - Store: s.store, - MlsStore: mlsStore, - AllowLister: s.allowLister, + Options: options.API, + Log: s.log.Named("`api"), + Waku: s.wakuNode, + Store: s.store, + MlsStore: mlsStore, + AllowLister: s.allowLister, + MlsValidator: mlsValidator, }, ) if err != nil { @@ -292,6 +306,9 @@ func (s *Server) Shutdown() { if s.store != nil { s.store.Close() } + if s.mlsStore != nil { + s.mlsStore.Close() + } // Close metrics server. if s.metricsServer != nil { @@ -510,6 +527,20 @@ func CreateAuthzMigration(migrationName, dbConnectionString string, waitForDb, r return err } +func CreateMlsMigration(migrationName, dbConnectionString string, waitForDb, readTimeout, writeTimeout time.Duration, maxOpenConns int) error { + db, err := createBunDB(dbConnectionString, waitForDb, readTimeout, writeTimeout, maxOpenConns) + if err != nil { + return err + } + migrator := migrate.NewMigrator(db, mlsmigrations.Migrations) + files, err := migrator.CreateSQLMigrations(context.Background(), migrationName) + for _, mf := range files { + fmt.Printf("created authz migration %s (%s)\n", mf.Name, mf.Path) + } + + return err +} + func createBunDB(dsn string, waitForDB, readTimeout, writeTimeout time.Duration, maxOpenConns int) (*bun.DB, error) { db, err := createDB(dsn, waitForDB, readTimeout, writeTimeout, maxOpenConns) if err != nil { diff --git a/pkg/testing/store.go b/pkg/testing/store.go index 35806f45..f9a2266c 100644 --- a/pkg/testing/store.go +++ b/pkg/testing/store.go @@ -11,6 +11,7 @@ import ( "github.com/uptrace/bun/driver/pgdriver" "github.com/uptrace/bun/migrate" "github.com/xmtp/xmtp-node-go/pkg/migrations/authz" + "github.com/xmtp/xmtp-node-go/pkg/migrations/mls" ) const ( @@ -48,3 +49,17 @@ func NewAuthzDB(t *testing.T) (*bun.DB, string, func()) { return bunDB, dsn, cleanup } + +func NewMlsDB(t *testing.T) (*bun.DB, string, func()) { + db, dsn, cleanup := NewDB(t) + bunDB := bun.NewDB(db, pgdialect.New()) + + ctx := context.Background() + migrator := migrate.NewMigrator(bunDB, mls.Migrations) + err := migrator.Init(ctx) + require.NoError(t, err) + _, err = migrator.Migrate(ctx) + require.NoError(t, err) + + return bunDB, dsn, cleanup +} From a3f7352ca8902201442c7ad298015a14fc3f3861 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 23 Oct 2023 14:11:08 -0700 Subject: [PATCH 07/33] Lint --- pkg/mlsstore/store_test.go | 2 +- pkg/mlsvalidate/service.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index 253b61ef..9aa66960 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -97,7 +97,7 @@ func TestInsertKeyPackages(t *testing.T) { require.NoError(t, err) keyPackagesFromDb := []*KeyPackage{} - store.db.NewSelect().Model(&keyPackagesFromDb).Where("installation_id = ?", installationId).Scan(ctx) + require.NoError(t, store.db.NewSelect().Model(&keyPackagesFromDb).Where("installation_id = ?", installationId).Scan(ctx)) require.Len(t, keyPackagesFromDb, 2) hasLastResort := false diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go index 2811e347..f0066933 100644 --- a/pkg/mlsvalidate/service.go +++ b/pkg/mlsvalidate/service.go @@ -2,7 +2,6 @@ package mlsvalidate import ( "context" - "errors" "fmt" svc "github.com/xmtp/proto/v3/go/mls_validation/v1" @@ -52,7 +51,7 @@ func (s *MlsValidationServiceImpl) ValidateKeyPackages(ctx context.Context, keyP out := make([]IdentityValidationResult, len(response.Responses)) for i, response := range response.Responses { if !response.IsOk { - return nil, errors.New(fmt.Sprintf("validation failed with error %s", response.ErrorMessage)) + return nil, fmt.Errorf("validation failed with error %s", response.ErrorMessage) } out[i] = IdentityValidationResult{ WalletAddress: response.WalletAddress, @@ -85,7 +84,7 @@ func (s *MlsValidationServiceImpl) ValidateGroupMessages(ctx context.Context, gr out := make([]GroupMessageValidationResult, len(response.Responses)) for i, response := range response.Responses { if !response.IsOk { - return nil, errors.New(fmt.Sprintf("validation failed with error %s", response.ErrorMessage)) + return nil, fmt.Errorf("validation failed with error %s", response.ErrorMessage) } out[i] = GroupMessageValidationResult{ GroupId: response.GroupId, From b833901c628dfd612200c4a8499dd7621926e8df Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 23 Oct 2023 14:18:02 -0700 Subject: [PATCH 08/33] Add new push action --- .github/workflows/push-mls.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/push-mls.yml diff --git a/.github/workflows/push-mls.yml b/.github/workflows/push-mls.yml new file mode 100644 index 00000000..8ad7f1e5 --- /dev/null +++ b/.github/workflows/push-mls.yml @@ -0,0 +1,34 @@ +name: Deploy Nodes +on: + push: + branches: + - mls +jobs: + deploy: + concurrency: main + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: xmtpeng + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Git Checkout + uses: actions/checkout@v3 + + - uses: actions/setup-go@v3 + with: + go-version-file: go.mod + + - name: Push + id: push + run: | + export DOCKER_IMAGE_TAG=mls + IMAGE_TO_DEPLOY=xmtp/node-go@$(dev/docker/build) + echo Successfully pushed $IMAGE_TO_DEPLOY + echo "docker_image=${IMAGE_TO_DEPLOY}" >> $GITHUB_OUTPUT From f9f1c4e70f667ffc308a749db5c22db463f0abac Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:52:11 -0700 Subject: [PATCH 09/33] Add a bunch of new endpoints --- pkg/api/message/v3/service.go | 209 ++++++++++++++++++-- pkg/api/message/v3/service_test.go | 302 +++++++++++++++++++++++++++++ pkg/api/server.go | 1 + pkg/mlsstore/store.go | 76 ++++++++ pkg/mlsstore/store_test.go | 117 ++++++++--- pkg/server/server.go | 6 +- pkg/store/query_test.go | 30 +++ pkg/store/store.go | 72 +++++++ pkg/testing/random.go | 6 + pkg/topic/topic.go | 13 +- 10 files changed, 777 insertions(+), 55 deletions(-) create mode 100644 pkg/api/message/v3/service_test.go diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 662f1a18..b395b6c1 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -2,12 +2,16 @@ package api import ( "context" + "sort" wakunode "github.com/waku-org/go-waku/waku/v2/node" + wakupb "github.com/waku-org/go-waku/waku/v2/protocol/pb" proto "github.com/xmtp/proto/v3/go/message_api/v3" + "github.com/xmtp/xmtp-node-go/pkg/metrics" "github.com/xmtp/xmtp-node-go/pkg/mlsstore" "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" "github.com/xmtp/xmtp-node-go/pkg/store" + "github.com/xmtp/xmtp-node-go/pkg/topic" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -22,9 +26,6 @@ type Service struct { messageStore *store.Store mlsStore mlsstore.MlsStore validationService mlsvalidate.MlsValidationService - - ctx context.Context - ctxCancel func() } func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore, validationService mlsvalidate.MlsValidationService) (s *Service, err error) { @@ -36,22 +37,20 @@ func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store validationService: validationService, } - s.ctx, s.ctxCancel = context.WithCancel(context.Background()) - + s.log.Info("Starting MLS service") return s, nil } -func (s *Service) Close() { - if s.ctxCancel != nil { - s.ctxCancel() +func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterInstallationRequest) (*proto.RegisterInstallationResponse, error) { + if err := validateRegisterInstallationRequest(req); err != nil { + return nil, err } -} -func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterInstallationRequest) (*proto.RegisterInstallationResponse, error) { results, err := s.validationService.ValidateKeyPackages(ctx, [][]byte{req.LastResortKeyPackage.KeyPackageTlsSerialized}) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid identity: %s", err) } + if len(results) != 1 { return nil, status.Errorf(codes.Internal, "unexpected number of results: %d", len(results)) } @@ -59,8 +58,7 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI installationId := results[0].InstallationId walletAddress := results[0].WalletAddress - err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) - if err != nil { + if err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized); err != nil { return nil, err } @@ -94,19 +92,87 @@ func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyP }, nil } -func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") +func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupRequest) (res *emptypb.Empty, err error) { + if err = validatePublishToGroupRequest(req); err != nil { + return nil, err + } + + messages := make([][]byte, len(req.Messages)) + for i, message := range req.Messages { + v1 := message.GetV1() + if v1 == nil { + return nil, status.Errorf(codes.InvalidArgument, "message must be v1") + } + messages[i] = v1.MlsMessageTlsSerialized + } + + validationResults, err := s.validationService.ValidateGroupMessages(ctx, messages) + if err != nil { + // TODO: Separate validation errors from internal errors + return nil, status.Errorf(codes.InvalidArgument, "invalid group message: %s", err) + } + + for i, result := range validationResults { + message := messages[i] + + if err = isReadyToSend(result.GroupId, message); err != nil { + return nil, err + } + + // TODO: Wrap this in a transaction so publishing is all or nothing + if err = s.publishMessage(ctx, topic.BuildGroupTopic(result.GroupId), message); err != nil { + return nil, status.Errorf(codes.Internal, "failed to publish message: %s", err) + } + } + + return &emptypb.Empty{}, nil } -func (s *Service) PublishWelcomes(ctx context.Context, req *proto.PublishWelcomesRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") +func (s *Service) publishMessage(ctx context.Context, contentTopic string, message []byte) error { + log := s.log.Named("publish-mls").With(zap.String("content_topic", contentTopic)) + env, err := s.messageStore.InsertMlsMessage(ctx, contentTopic, message) + if err != nil { + return status.Errorf(codes.Internal, "failed to insert message: %s", err) + } + + if _, err = s.waku.Relay().Publish(ctx, &wakupb.WakuMessage{ + ContentTopic: contentTopic, + Timestamp: int64(env.TimestampNs), + Payload: message, + }); err != nil { + return status.Errorf(codes.Internal, "failed to publish message: %s", err) + } + + metrics.EmitPublishedEnvelope(ctx, log, env) + + return nil +} + +func (s *Service) PublishWelcomes(ctx context.Context, req *proto.PublishWelcomesRequest) (res *emptypb.Empty, err error) { + if err = validatePublishWelcomesRequest(req); err != nil { + return nil, err + } + + // TODO: Wrap this in a transaction so publishing is all or nothing + for _, welcome := range req.WelcomeMessages { + contentTopic := topic.BuildWelcomeTopic(welcome.InstallationId) + if err = s.publishMessage(ctx, contentTopic, welcome.WelcomeMessage.GetV1().Ciphertext); err != nil { + return nil, status.Errorf(codes.Internal, "failed to publish welcome message: %s", err) + } + } + return &emptypb.Empty{}, nil } -func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPackagesRequest) (*emptypb.Empty, error) { +func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPackagesRequest) (res *emptypb.Empty, err error) { + if err = validateUploadKeyPackagesRequest(req); err != nil { + return nil, err + } + // Extract the key packages from the request keyPackageBytes := make([][]byte, len(req.KeyPackages)) for i, keyPackage := range req.KeyPackages { keyPackageBytes[i] = keyPackage.KeyPackageTlsSerialized } + validationResults, err := s.validationService.ValidateKeyPackages(ctx, keyPackageBytes) if err != nil { // TODO: Differentiate between validation errors and internal errors @@ -118,8 +184,8 @@ func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPac kp := mlsstore.NewKeyPackage(validationResult.InstallationId, keyPackageBytes[i], false) keyPackageModels[i] = &kp } - err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels) - if err != nil { + + if err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels); err != nil { return nil, status.Errorf(codes.Internal, "failed to insert key packages: %s", err) } @@ -130,8 +196,109 @@ func (s *Service) RevokeInstallation(ctx context.Context, req *proto.RevokeInsta return nil, status.Errorf(codes.Unimplemented, "unimplemented") } -func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentityUpdatesRequest) (*proto.GetIdentityUpdatesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "unimplemented") +func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentityUpdatesRequest) (res *proto.GetIdentityUpdatesResponse, err error) { + if err = validateGetIdentityUpdatesRequest(req); err != nil { + return nil, err + } + + walletAddresses := req.WalletAddresses + updates, err := s.mlsStore.GetIdentityUpdates(ctx, req.WalletAddresses, int64(req.StartTimeNs)) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get identity updates: %s", err) + } + + resUpdates := make([]*proto.GetIdentityUpdatesResponse_WalletUpdates, len(walletAddresses)) + for i, walletAddress := range walletAddresses { + walletUpdates := updates[walletAddress] + sort.Sort(walletUpdates) + resUpdates[i] = &proto.GetIdentityUpdatesResponse_WalletUpdates{ + Updates: []*proto.GetIdentityUpdatesResponse_Update{}, + } + + for _, walletUpdate := range walletUpdates { + resUpdates[i].Updates = append(resUpdates[i].Updates, buildIdentityUpdate(walletUpdate)) + } + } + + return &proto.GetIdentityUpdatesResponse{ + Updates: resUpdates, + }, nil +} + +func buildIdentityUpdate(update mlsstore.IdentityUpdate) *proto.GetIdentityUpdatesResponse_Update { + base := proto.GetIdentityUpdatesResponse_Update{ + TimestampNs: update.TimestampNs, + } + switch update.Kind { + case mlsstore.Create: + base.Kind = &proto.GetIdentityUpdatesResponse_Update_NewInstallation{ + NewInstallation: &proto.GetIdentityUpdatesResponse_NewInstallationUpdate{ + InstallationId: update.InstallationId, + }, + } + case mlsstore.Revoke: + base.Kind = &proto.GetIdentityUpdatesResponse_Update_RevokedInstallation{ + RevokedInstallation: &proto.GetIdentityUpdatesResponse_RevokedInstallationUpdate{ + InstallationId: update.InstallationId, + }, + } + } + + return &base +} + +func validatePublishToGroupRequest(req *proto.PublishToGroupRequest) error { + if req == nil || len(req.Messages) == 0 { + return status.Errorf(codes.InvalidArgument, "no messages to publish") + } + return nil +} + +func validatePublishWelcomesRequest(req *proto.PublishWelcomesRequest) error { + if req == nil || len(req.WelcomeMessages) == 0 { + return status.Errorf(codes.InvalidArgument, "no welcome messages to publish") + } + for _, welcome := range req.WelcomeMessages { + if welcome == nil || welcome.WelcomeMessage == nil { + return status.Errorf(codes.InvalidArgument, "invalid welcome message") + } + ciphertext := welcome.WelcomeMessage.GetV1().Ciphertext + if ciphertext == nil || len(ciphertext) == 0 { + return status.Errorf(codes.InvalidArgument, "invalid welcome message") + } + } + return nil +} + +func validateRegisterInstallationRequest(req *proto.RegisterInstallationRequest) error { + if req == nil || req.LastResortKeyPackage == nil { + return status.Errorf(codes.InvalidArgument, "no last resort key package") + } + return nil +} + +func validateUploadKeyPackagesRequest(req *proto.UploadKeyPackagesRequest) error { + if req == nil || len(req.KeyPackages) == 0 { + return status.Errorf(codes.InvalidArgument, "no key packages to upload") + } + return nil +} + +func validateGetIdentityUpdatesRequest(req *proto.GetIdentityUpdatesRequest) error { + if req == nil || len(req.WalletAddresses) == 0 { + return status.Errorf(codes.InvalidArgument, "no wallet addresses to get updates for") + } + return nil +} + +func isReadyToSend(groupId string, message []byte) error { + if groupId == "" { + return status.Errorf(codes.InvalidArgument, "group id is empty") + } + if len(message) == 0 { + return status.Errorf(codes.InvalidArgument, "message is empty") + } + return nil } func indexOf(target string, ids []string) int { diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go new file mode 100644 index 00000000..a0b92075 --- /dev/null +++ b/pkg/api/message/v3/service_test.go @@ -0,0 +1,302 @@ +package api + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/uptrace/bun" + v1 "github.com/xmtp/proto/v3/go/message_api/v1" + proto "github.com/xmtp/proto/v3/go/message_api/v3" + messageContents "github.com/xmtp/proto/v3/go/mls/message_contents" + "github.com/xmtp/xmtp-node-go/pkg/mlsstore" + "github.com/xmtp/xmtp-node-go/pkg/mlsvalidate" + "github.com/xmtp/xmtp-node-go/pkg/store" + test "github.com/xmtp/xmtp-node-go/pkg/testing" +) + +type mockedMlsValidationService struct { + mock.Mock +} + +func (m *mockedMlsValidationService) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]mlsvalidate.IdentityValidationResult, error) { + args := m.Called(ctx, keyPackages) + + return args.Get(0).([]mlsvalidate.IdentityValidationResult), args.Error(1) +} + +func (m *mockedMlsValidationService) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]mlsvalidate.GroupMessageValidationResult, error) { + args := m.Called(ctx, groupMessages) + + return args.Get(0).([]mlsvalidate.GroupMessageValidationResult), args.Error(1) +} + +func newMockedValidationService() *mockedMlsValidationService { + return new(mockedMlsValidationService) +} + +func (m *mockedMlsValidationService) mockValidateKeyPackages(installationId, walletAddress string) *mock.Call { + return m.On("ValidateKeyPackages", mock.Anything, mock.Anything).Return([]mlsvalidate.IdentityValidationResult{ + { + InstallationId: installationId, + WalletAddress: walletAddress, + }, + }, nil) +} + +func (m *mockedMlsValidationService) mockValidateGroupMessages(groupId string) *mock.Call { + return m.On("ValidateGroupMessages", mock.Anything, mock.Anything).Return([]mlsvalidate.GroupMessageValidationResult{ + { + GroupId: groupId, + }, + }, nil) +} + +func newTestService(t *testing.T, ctx context.Context) (*Service, *bun.DB, *mockedMlsValidationService, func()) { + log := test.NewLog(t) + mlsDb, _, mlsDbCleanup := test.NewMlsDB(t) + mlsStore, err := mlsstore.New(ctx, mlsstore.Config{ + Log: log, + DB: mlsDb, + }) + require.NoError(t, err) + messageDb, _, messageDbCleanup := test.NewDB(t) + messageStore, err := store.New(&store.Config{ + Log: log, + DB: messageDb, + ReaderDB: messageDb, + CleanerDB: messageDb, + }) + node, nodeCleanup := test.NewNode(t) + mlsValidationService := newMockedValidationService() + + svc, err := NewService(node, log, messageStore, mlsStore, mlsValidationService) + + return svc, mlsDb, mlsValidationService, func() { + messageStore.Close() + mlsDbCleanup() + messageDbCleanup() + nodeCleanup() + } +} + +func TestRegisterInstallation(t *testing.T) { + ctx := context.Background() + svc, mlsDb, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + + mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) + + res, err := svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + + require.NoError(t, err) + require.Equal(t, installationId, res.InstallationId) + + installations := []mlsstore.Installation{} + err = mlsDb.NewSelect().Model(&installations).Where("id = ?", installationId).Scan(ctx) + require.NoError(t, err) + + require.Len(t, installations, 1) + require.Equal(t, walletAddress, installations[0].WalletAddress) +} + +func TestRegisterInstallationError(t *testing.T) { + ctx := context.Background() + svc, _, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + mlsValidationService.On("ValidateKeyPackages", ctx, mock.Anything).Return(nil, errors.New("error validating")) + + res, err := svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + require.Error(t, err) + require.Nil(t, res) +} + +func TestUploadKeyPackages(t *testing.T) { + ctx := context.Background() + svc, mlsDb, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + + mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) + + res, err := svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + require.NoError(t, err) + require.NotNil(t, res) + + uploadRes, err := svc.UploadKeyPackages(ctx, &proto.UploadKeyPackagesRequest{ + KeyPackages: []*proto.KeyPackageUpload{ + {KeyPackageTlsSerialized: []byte("test2")}, + }, + }) + require.NoError(t, err) + require.NotNil(t, uploadRes) + + keyPackages := []mlsstore.KeyPackage{} + err = mlsDb.NewSelect().Model(&keyPackages).Where("installation_id = ?", installationId).Scan(ctx) + require.NoError(t, err) + require.Len(t, keyPackages, 2) +} + +func TestConsumeKeyPackages(t *testing.T) { + ctx := context.Background() + svc, _, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + installationId1 := test.RandomString(32) + walletAddress1 := test.RandomString(32) + + mockCall := mlsValidationService.mockValidateKeyPackages(installationId1, walletAddress1) + + res, err := svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + require.NoError(t, err) + require.NotNil(t, res) + + // Add a second key package + installationId2 := test.RandomString(32) + walletAddress2 := test.RandomString(32) + // Unset the original mock so we can set a new one + mockCall.Unset() + mlsValidationService.mockValidateKeyPackages(installationId2, walletAddress2) + + res, err = svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test2"), + }, + }) + require.NoError(t, err) + require.NotNil(t, res) + + consumeRes, err := svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ + InstallationIds: []string{installationId1, installationId2}, + }) + require.NoError(t, err) + require.NotNil(t, consumeRes) + require.Len(t, consumeRes.KeyPackages, 2) + require.Equal(t, []byte("test"), consumeRes.KeyPackages[0].KeyPackageTlsSerialized) + require.Equal(t, []byte("test2"), consumeRes.KeyPackages[1].KeyPackageTlsSerialized) + + // Now do it with the installationIds reversed + consumeRes, err = svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ + InstallationIds: []string{installationId2, installationId1}, + }) + + require.NoError(t, err) + require.NotNil(t, consumeRes) + require.Len(t, consumeRes.KeyPackages, 2) + require.Equal(t, []byte("test2"), consumeRes.KeyPackages[0].KeyPackageTlsSerialized) + require.Equal(t, []byte("test"), consumeRes.KeyPackages[1].KeyPackageTlsSerialized) +} + +// Trying to consume key packages that don't exist should fail +func TestConsumeKeyPackagesFail(t *testing.T) { + ctx := context.Background() + svc, _, _, cleanup := newTestService(t, ctx) + defer cleanup() + + consumeRes, err := svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ + InstallationIds: []string{test.RandomString(32)}, + }) + require.Error(t, err) + require.Nil(t, consumeRes) +} + +func TestPublishToGroup(t *testing.T) { + ctx := context.Background() + svc, _, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + groupId := test.RandomString(32) + + mlsValidationService.mockValidateGroupMessages(groupId) + + _, err := svc.PublishToGroup(ctx, &proto.PublishToGroupRequest{ + Messages: []*messageContents.GroupMessage{{ + Version: &messageContents.GroupMessage_V1_{ + V1: &messageContents.GroupMessage_V1{ + MlsMessageTlsSerialized: []byte("test"), + }, + }, + }}, + }) + require.NoError(t, err) + + results, err := svc.messageStore.Query(&v1.QueryRequest{ + ContentTopics: []string{fmt.Sprintf("/xmtp/3/g-%s/proto", groupId)}, + }) + require.NoError(t, err) + require.Len(t, results.Envelopes, 1) + require.Equal(t, results.Envelopes[0].Message, []byte("test")) + require.NotNil(t, results.Envelopes[0].TimestampNs) +} + +func TestGetIdentityUpdates(t *testing.T) { + ctx := context.Background() + svc, _, mlsValidationService, cleanup := newTestService(t, ctx) + defer cleanup() + + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + + mockCall := mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) + + _, err := svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + require.NoError(t, err) + + identityUpdates, err := svc.GetIdentityUpdates(ctx, &proto.GetIdentityUpdatesRequest{ + WalletAddresses: []string{walletAddress}, + }) + require.NoError(t, err) + require.NotNil(t, identityUpdates) + require.Len(t, identityUpdates.Updates, 1) + + for _, walletUpdate := range identityUpdates.Updates { + for _, update := range walletUpdate.Updates { + require.Equal(t, installationId, update.GetNewInstallation().InstallationId) + } + } + + mockCall.Unset() + mlsValidationService.mockValidateKeyPackages(test.RandomString(32), walletAddress) + _, err = svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ + LastResortKeyPackage: &proto.KeyPackageUpload{ + KeyPackageTlsSerialized: []byte("test"), + }, + }) + require.NoError(t, err) + + identityUpdates, err = svc.GetIdentityUpdates(ctx, &proto.GetIdentityUpdatesRequest{ + WalletAddresses: []string{walletAddress}, + }) + require.NoError(t, err) + require.Len(t, identityUpdates.Updates, 1) + require.Len(t, identityUpdates.Updates[0].Updates, 2) +} diff --git a/pkg/api/server.go b/pkg/api/server.go index 8af7c951..dea623d5 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -131,6 +131,7 @@ func (s *Server) startGRPC() error { // Enable the MLS server if a store is provided if s.Config.MlsStore != nil && s.Config.MlsValidator != nil && s.Config.EnableMls { + s.Log.Info("Ready to start MLS service") s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MlsStore, s.Config.MlsValidator) if err != nil { return errors.Wrap(err, "creating mls service") diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index bfc91701..6347f01d 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -24,6 +24,7 @@ type MlsStore interface { CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage) error ConsumeKeyPackages(ctx context.Context, installationIds []string) ([]*KeyPackage, error) + GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) } func New(ctx context.Context, config Config) (*Store, error) { @@ -124,6 +125,81 @@ func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string return keyPackages, nil } +type IdentityUpdateKind int + +const ( + Create IdentityUpdateKind = iota + Revoke +) + +type IdentityUpdate struct { + Kind IdentityUpdateKind + InstallationId string + TimestampNs uint64 +} + +// Add the required methods to make a valid sort.Sort interface +type IdentityUpdateList []IdentityUpdate + +func (a IdentityUpdateList) Len() int { + return len(a) +} + +func (a IdentityUpdateList) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a IdentityUpdateList) Less(i, j int) bool { + return a[i].TimestampNs < a[j].TimestampNs +} + +func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) { + updated := make([]*Installation, 0) + err := s.db.NewSelect(). + Model(&updated). + Where("wallet_address IN (?)", bun.In(walletAddresses)). + WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { + return q.Where("created_at > ?", startTimeNs).WhereOr("revoked_at > ?", startTimeNs) + }). + Order("created_at ASC"). + Scan(ctx) + + if err != nil { + return nil, err + } + + out := make(map[string]IdentityUpdateList) + for _, installation := range updated { + if installation.CreatedAt > startTimeNs { + out[installation.WalletAddress] = append(out[installation.WalletAddress], IdentityUpdate{ + Kind: Create, + InstallationId: installation.ID, + TimestampNs: uint64(installation.CreatedAt), + }) + } + if installation.RevokedAt != nil && *installation.RevokedAt > startTimeNs { + out[installation.WalletAddress] = append(out[installation.WalletAddress], IdentityUpdate{ + Kind: Revoke, + InstallationId: installation.ID, + TimestampNs: uint64(*installation.RevokedAt), + }) + } + } + + return out, nil +} + +func (s *Store) RevokeInstallation(ctx context.Context, installationId string) error { + _, err := s.db.NewUpdate(). + Model(&Installation{}). + Set("revoked_at = ?", nowNs()). + Where("id = ?", installationId). + Where("revoked_at IS NULL"). + Exec(ctx) + + return err +} + func NewKeyPackage(installationId string, data []byte, isLastResort bool) KeyPackage { return KeyPackage{ ID: buildKeyPackageId(data), diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index 9aa66960..a2ca993b 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -2,8 +2,6 @@ package mlsstore import ( "context" - "crypto/rand" - "fmt" "testing" "github.com/stretchr/testify/require" @@ -25,25 +23,15 @@ func NewTestStore(t *testing.T) (*Store, func()) { return store, dbCleanup } -func randomBytes(n int) []byte { - b := make([]byte, n) - _, _ = rand.Reader.Read(b) - return b -} - -func randomString(n int) string { - return fmt.Sprintf("%x", randomBytes(n)) -} - func TestCreateInstallation(t *testing.T) { store, cleanup := NewTestStore(t) defer cleanup() ctx := context.Background() - installationId := randomString(32) - walletAddress := randomString(32) + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, randomBytes(32)) + err := store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32)) require.NoError(t, err) installationFromDb := &Installation{} @@ -60,13 +48,13 @@ func TestCreateInstallationIdempotent(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := randomString(32) - walletAddress := randomString(32) - keyPackage := randomBytes(32) + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + keyPackage := test.RandomBytes(32) err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) require.NoError(t, err) - err = store.CreateInstallation(ctx, installationId, walletAddress, randomBytes(32)) + err = store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32)) require.NoError(t, err) keyPackageFromDb := &KeyPackage{} @@ -79,14 +67,14 @@ func TestInsertKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := randomString(32) - walletAddress := randomString(32) - keyPackage := randomBytes(32) + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + keyPackage := test.RandomBytes(32) err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) require.NoError(t, err) - keyPackage2 := randomBytes(32) + keyPackage2 := test.RandomBytes(32) err = store.InsertKeyPackages(ctx, []*KeyPackage{{ ID: buildKeyPackageId(keyPackage2), InstallationId: installationId, @@ -122,9 +110,9 @@ func TestConsumeLastResortKeyPackage(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := randomString(32) - walletAddress := randomString(32) - keyPackage := randomBytes(32) + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + keyPackage := test.RandomBytes(32) err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) require.NoError(t, err) @@ -141,14 +129,14 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := randomString(32) - walletAddress := randomString(32) - keyPackage := randomBytes(32) + installationId := test.RandomString(32) + walletAddress := test.RandomString(32) + keyPackage := test.RandomBytes(32) err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) require.NoError(t, err) - keyPackage2 := randomBytes(32) + keyPackage2 := test.RandomBytes(32) require.NoError(t, store.InsertKeyPackages(ctx, []*KeyPackage{{ ID: buildKeyPackageId(keyPackage2), InstallationId: installationId, @@ -169,3 +157,72 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { // Now we are out of regular key packages. Expect to consume the last resort require.Equal(t, keyPackage, consumeResult[0].Data) } + +func TestGetIdentityUpdates(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + walletAddress := test.RandomString(32) + + installationId1 := test.RandomString(32) + keyPackage1 := test.RandomBytes(32) + + err := store.CreateInstallation(ctx, installationId1, walletAddress, keyPackage1) + require.NoError(t, err) + + installationId2 := test.RandomString(32) + keyPackage2 := test.RandomBytes(32) + + err = store.CreateInstallation(ctx, installationId2, walletAddress, keyPackage2) + require.NoError(t, err) + + identityUpdates, err := store.GetIdentityUpdates(ctx, []string{walletAddress}, 0) + require.NoError(t, err) + require.Len(t, identityUpdates[walletAddress], 2) + require.Equal(t, identityUpdates[walletAddress][0].InstallationId, installationId1) + require.Equal(t, identityUpdates[walletAddress][0].Kind, Create) + require.Equal(t, identityUpdates[walletAddress][1].InstallationId, installationId2) + + // Make sure that date filtering works + identityUpdates, err = store.GetIdentityUpdates(ctx, []string{walletAddress}, nowNs()+1000000) + require.NoError(t, err) + require.Len(t, identityUpdates[walletAddress], 0) +} + +func TestGetIdentityUpdatesMultipleWallets(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + walletAddress1 := test.RandomString(32) + installationId1 := test.RandomString(32) + keyPackage1 := test.RandomBytes(32) + + err := store.CreateInstallation(ctx, installationId1, walletAddress1, keyPackage1) + require.NoError(t, err) + + walletAddress2 := test.RandomString(32) + installationId2 := test.RandomString(32) + keyPackage2 := test.RandomBytes(32) + + err = store.CreateInstallation(ctx, installationId2, walletAddress2, keyPackage2) + require.NoError(t, err) + + identityUpdates, err := store.GetIdentityUpdates(ctx, []string{walletAddress1, walletAddress2}, 0) + require.NoError(t, err) + require.Len(t, identityUpdates[walletAddress1], 1) + require.Len(t, identityUpdates[walletAddress2], 1) +} + +func TestGetIdentityUpdatesNoResult(t *testing.T) { + store, cleanup := NewTestStore(t) + defer cleanup() + + ctx := context.Background() + walletAddress := test.RandomString(32) + + identityUpdates, err := store.GetIdentityUpdates(ctx, []string{walletAddress}, 0) + require.NoError(t, err) + require.Len(t, identityUpdates[walletAddress], 0) +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 75747414..f6563893 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -229,14 +229,14 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } s.log.With(logging.MultiAddrs("listen", maddrs...)).Info("got server") - var mlsStore mlsstore.MlsStore - if options.MlsStore.DbConnectionString != "" { + s.log.Info("creating mls db") mlsDb, err := createBunDB(options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.MlsStore.MaxOpenConns) if err != nil { return nil, errors.Wrap(err, "creating mls db") } + s.log.Info("creating mls store") s.mlsStore, err = mlsstore.New(s.ctx, mlsstore.Config{ Log: s.log, DB: mlsDb, @@ -263,7 +263,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) Log: s.log.Named("`api"), Waku: s.wakuNode, Store: s.store, - MlsStore: mlsStore, + MlsStore: s.mlsStore, AllowLister: s.allowLister, MlsValidator: mlsValidator, }, diff --git a/pkg/store/query_test.go b/pkg/store/query_test.go index 5d084e4a..afd087cf 100644 --- a/pkg/store/query_test.go +++ b/pkg/store/query_test.go @@ -1,6 +1,7 @@ package store import ( + "context" "testing" "time" @@ -284,3 +285,32 @@ func TestPageSizeOne(t *testing.T) { loops++ } } + +func TestMlsMessagePublish(t *testing.T) { + store, cleanup, _ := createAndFillDb(t) + defer cleanup() + + message := []byte{1, 2, 3} + contentTopic := "foo" + ctx := context.Background() + + env, err := store.InsertMlsMessage(ctx, contentTopic, message) + require.NoError(t, err) + + require.Equal(t, env.ContentTopic, contentTopic) + require.Equal(t, env.Message, message) + + response, err := store.Query(&messagev1.QueryRequest{ + ContentTopics: []string{contentTopic}, + }) + require.Len(t, response.Envelopes, 1) + require.Equal(t, response.Envelopes[0].Message, message) + require.Equal(t, response.Envelopes[0].ContentTopic, contentTopic) + require.NotNil(t, response.Envelopes[0].TimestampNs) + + parsedTime := time.Unix(0, int64(response.Envelopes[0].TimestampNs)) + // Sanity check to ensure that the timestamps are reasonable + require.True(t, time.Since(parsedTime) < 10*time.Second || time.Since(parsedTime) > -10*time.Second) + + require.Equal(t, env.TimestampNs, response.Envelopes[0].TimestampNs) +} diff --git a/pkg/store/store.go b/pkg/store/store.go index 71e17d9a..b697b7d1 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -2,6 +2,7 @@ package store import ( "context" + "fmt" "strings" "sync" "time" @@ -20,6 +21,22 @@ import ( const maxPageSize = 100 +const timestampGeneratorSql = `( + ( + EXTRACT( + EPOCH + FROM + clock_timestamp() + ) :: bigint * 1000000000 + ) + ( + EXTRACT( + MICROSECONDS + FROM + clock_timestamp() + ) :: bigint * 1000 + ) +)` + type Store struct { config *Config ctx context.Context @@ -127,6 +144,61 @@ func (s *Store) InsertMessage(env *messagev1.Envelope) (bool, error) { return stored, err } +func (s *Store) InsertMlsMessage(ctx context.Context, contentTopic string, data []byte) (*messagev1.Envelope, error) { + tmpEnvelope := &messagev1.Envelope{ + ContentTopic: contentTopic, + Message: data, + } + digest := computeDigest(tmpEnvelope) + var envelope messagev1.Envelope + + err := tracing.Wrap(s.ctx, s.log, "storing mls message", func(ctx context.Context, log *zap.Logger, span tracing.Span) error { + tracing.SpanResource(span, "store") + tracing.SpanType(span, "db") + + stmnt := fmt.Sprintf(`INSERT INTO + message ( + id, + receiverTimestamp, + senderTimestamp, + contentTopic, + pubsubTopic, + payload, + version, + should_expire + ) + VALUES + ( + $1, + %s, + %s, + $2, + $3, + $4, + $5, + $6 + ) RETURNING senderTimestamp`, timestampGeneratorSql, timestampGeneratorSql) + + var senderTimestamp uint64 + err := s.config.DB.QueryRowContext(ctx, stmnt, digest, contentTopic, "", data, 0, false).Scan(&senderTimestamp) + if err != nil { + return err + } + envelope = messagev1.Envelope{ + ContentTopic: contentTopic, + TimestampNs: senderTimestamp, + Message: data, + } + return err + }) + + if err != nil { + return nil, err + } + + return &envelope, nil +} + func (s *Store) insertMessage(env *messagev1.Envelope, receiverTimestamp int64) error { digest := computeDigest(env) shouldExpire := !isXMTP(env.ContentTopic) diff --git a/pkg/testing/random.go b/pkg/testing/random.go index e96427c4..3d0d4e9c 100644 --- a/pkg/testing/random.go +++ b/pkg/testing/random.go @@ -18,3 +18,9 @@ func RandomString(n int) string { func RandomStringLower(n int) string { return strings.ToLower(RandomString(n)) } + +func RandomBytes(n int) []byte { + b := make([]byte, n) + _, _ = rand.Read(b) + return b +} diff --git a/pkg/topic/topic.go b/pkg/topic/topic.go index 0bc16df0..473f141b 100644 --- a/pkg/topic/topic.go +++ b/pkg/topic/topic.go @@ -1,6 +1,9 @@ package topic -import "strings" +import ( + "fmt" + "strings" +) var topicCategoryByPrefix = map[string]string{ "test": "test", @@ -35,3 +38,11 @@ func Category(contentTopic string) string { } return "invalid" } + +func BuildGroupTopic(groupId string) string { + return fmt.Sprintf("/xmtp/3/g-%s/proto", groupId) +} + +func BuildWelcomeTopic(installationId string) string { + return fmt.Sprintf("/xmtp/3/w-%s/proto", installationId) +} From 8c94283694ce1c88ade889679167eaa5b56c07a0 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:38:58 -0700 Subject: [PATCH 10/33] Address review comments --- pkg/api/message/v3/service.go | 14 +++++++++----- .../mls/20231023050806_init-schema.up.sql | 16 ++++++++-------- pkg/mlsstore/models.go | 1 + pkg/mlsstore/store.go | 8 +++++--- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 662f1a18..02cf72d0 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -75,16 +75,20 @@ func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyP if err != nil { return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) } + keyPackageMap := make(map[string]int) + for idx, id := range ids { + keyPackageMap[id] = idx + } resPackages := make([]*proto.ConsumeKeyPackagesResponse_KeyPackage, len(keyPackages)) for _, keyPackage := range keyPackages { - // Return the key packages in the original order - targetIndex := indexOf(keyPackage.InstallationId, ids) - if targetIndex == -1 { + + idx, ok := keyPackageMap[keyPackage.InstallationId] + if !ok { return nil, status.Errorf(codes.Internal, "could not find key package for installation") } - resPackages[targetIndex] = &proto.ConsumeKeyPackagesResponse_KeyPackage{ + resPackages[idx] = &proto.ConsumeKeyPackagesResponse_KeyPackage{ KeyPackageTlsSerialized: keyPackage.Data, } } @@ -116,7 +120,7 @@ func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPac keyPackageModels := make([]*mlsstore.KeyPackage, len(validationResults)) for i, validationResult := range validationResults { kp := mlsstore.NewKeyPackage(validationResult.InstallationId, keyPackageBytes[i], false) - keyPackageModels[i] = &kp + keyPackageModels[i] = kp } err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels) if err != nil { diff --git a/pkg/migrations/mls/20231023050806_init-schema.up.sql b/pkg/migrations/mls/20231023050806_init-schema.up.sql index 193d5474..b6aa90d5 100644 --- a/pkg/migrations/mls/20231023050806_init-schema.up.sql +++ b/pkg/migrations/mls/20231023050806_init-schema.up.sql @@ -15,6 +15,7 @@ CREATE TABLE key_packages ( installation_id TEXT NOT NULL, created_at BIGINT NOT NULL, consumed_at BIGINT, + not_consumed BOOLEAN DEFAULT TRUE NOT NULL, is_last_resort BOOLEAN NOT NULL, data BYTEA NOT NULL, -- Add a foreign key constraint to ensure key packages cannot be added for unregistered installations @@ -32,13 +33,12 @@ CREATE INDEX idx_installations_revoked_at ON installations(revoked_at); --bun:split -- Adding indexes for the key_packages table -CREATE INDEX idx_key_packages_installation_id ON key_packages(installation_id); - ---bun:split -CREATE INDEX idx_key_packages_created_at ON key_packages(created_at); - ---bun:split -CREATE INDEX idx_key_packages_consumed_at ON key_packages(consumed_at); +CREATE INDEX idx_key_packages_installation_id_not_is_last_resort_created_at ON key_packages( + installation_id, + not_consumed, + is_last_resort, + created_at +); --bun:split -CREATE INDEX idx_key_packages_is_last_resort ON key_packages(is_last_resort); \ No newline at end of file +CREATE INDEX idx_key_packages_is_last_resort_id ON key_packages(is_last_resort, id); \ No newline at end of file diff --git a/pkg/mlsstore/models.go b/pkg/mlsstore/models.go index 4df764bc..533c595d 100644 --- a/pkg/mlsstore/models.go +++ b/pkg/mlsstore/models.go @@ -18,6 +18,7 @@ type KeyPackage struct { InstallationId string `bun:"installation_id,notnull"` CreatedAt int64 `bun:"created_at,notnull"` ConsumedAt *int64 `bun:"consumed_at"` + NotConsumed bool `bun:"not_consumed,default:true"` IsLastResort bool `bun:"is_last_resort,notnull"` Data []byte `bun:"data,notnull,type:bytea"` } diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index bfc91701..3d539338 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -93,7 +93,7 @@ func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string err := tx.NewRaw(` SELECT DISTINCT ON(installation_id) * FROM key_packages WHERE "installation_id" IN (?) - AND "consumed_at" IS NULL + AND not_consumed = TRUE ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC `, bun.In(installationIds)). @@ -110,6 +110,7 @@ func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string _, err = tx.NewUpdate(). Table("key_packages"). Set("consumed_at = ?", nowNs()). + Set("not_consumed = FALSE"). Where("is_last_resort = FALSE"). Where("id IN (?)", bun.In(extractIds(keyPackages))). Exec(ctx) @@ -124,12 +125,13 @@ func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string return keyPackages, nil } -func NewKeyPackage(installationId string, data []byte, isLastResort bool) KeyPackage { - return KeyPackage{ +func NewKeyPackage(installationId string, data []byte, isLastResort bool) *KeyPackage { + return &KeyPackage{ ID: buildKeyPackageId(data), InstallationId: installationId, CreatedAt: nowNs(), IsLastResort: isLastResort, + NotConsumed: true, Data: data, } } From da110346801eb3014f2c6f4c0bf08f1eb3c69df4 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:44:26 -0700 Subject: [PATCH 11/33] Change method casing --- pkg/api/config.go | 4 ++-- pkg/api/message/v3/service.go | 14 +++++++------- pkg/api/server.go | 6 +++--- pkg/mlsstore/store_test.go | 6 +++--- pkg/mlsvalidate/service.go | 12 ++++++------ pkg/mlsvalidate/service_test.go | 12 ++++++------ pkg/server/server.go | 18 +++++++++--------- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/pkg/api/config.go b/pkg/api/config.go index be09a094..723679bf 100644 --- a/pkg/api/config.go +++ b/pkg/api/config.go @@ -36,8 +36,8 @@ type Config struct { Waku *wakunode.WakuNode Log *zap.Logger Store *store.Store - MlsStore mlsstore.MlsStore - MlsValidator mlsvalidate.MlsValidationService + MLSStore mlsstore.MlsStore + MLSValidator mlsvalidate.MLSValidationService } // AuthnOptions bundle command line options associated with the authn package. diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 02cf72d0..c0a64def 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -20,19 +20,19 @@ type Service struct { log *zap.Logger waku *wakunode.WakuNode messageStore *store.Store - mlsStore mlsstore.MlsStore - validationService mlsvalidate.MlsValidationService + MLSStore mlsstore.MlsStore + validationService mlsvalidate.MLSValidationService ctx context.Context ctxCancel func() } -func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore, validationService mlsvalidate.MlsValidationService) (s *Service, err error) { +func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store.Store, mlsStore mlsstore.MlsStore, validationService mlsvalidate.MLSValidationService) (s *Service, err error) { s = &Service{ log: logger.Named("message/v3"), waku: node, messageStore: messageStore, - mlsStore: mlsStore, + MLSStore: mlsStore, validationService: validationService, } @@ -59,7 +59,7 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI installationId := results[0].InstallationId walletAddress := results[0].WalletAddress - err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) + err = s.MLSStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { ids := req.InstallationIds - keyPackages, err := s.mlsStore.ConsumeKeyPackages(ctx, ids) + keyPackages, err := s.MLSStore.ConsumeKeyPackages(ctx, ids) if err != nil { return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) } @@ -122,7 +122,7 @@ func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPac kp := mlsstore.NewKeyPackage(validationResult.InstallationId, keyPackageBytes[i], false) keyPackageModels[i] = kp } - err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels) + err = s.MLSStore.InsertKeyPackages(ctx, keyPackageModels) if err != nil { return nil, status.Errorf(codes.Internal, "failed to insert key packages: %s", err) } diff --git a/pkg/api/server.go b/pkg/api/server.go index 8af7c951..9648c3a1 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -130,8 +130,8 @@ func (s *Server) startGRPC() error { proto.RegisterMessageApiServer(grpcServer, s.messagev1) // Enable the MLS server if a store is provided - if s.Config.MlsStore != nil && s.Config.MlsValidator != nil && s.Config.EnableMls { - s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MlsStore, s.Config.MlsValidator) + if s.Config.MLSStore != nil && s.Config.MLSValidator != nil && s.Config.EnableMls { + s.messagev3, err = messagev3.NewService(s.Waku, s.Log, s.Store, s.Config.MLSStore, s.Config.MLSValidator) if err != nil { return errors.Wrap(err, "creating mls service") } @@ -183,7 +183,7 @@ func (s *Server) startHTTP() error { return errors.Wrap(err, "registering message handler") } - if s.Config.MlsStore != nil && s.Config.EnableMls { + if s.Config.MLSStore != nil && s.Config.EnableMls { err = v3Proto.RegisterMlsApiHandler(s.ctx, gwmux, conn) if err != nil { return errors.Wrap(err, "registering mls handler") diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index 9aa66960..026b3da9 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -50,9 +50,9 @@ func TestCreateInstallation(t *testing.T) { require.NoError(t, store.db.NewSelect().Model(installationFromDb).Where("id = ?", installationId).Scan(ctx)) require.Equal(t, walletAddress, installationFromDb.WalletAddress) - keyPackageFromDb := &KeyPackage{} - require.NoError(t, store.db.NewSelect().Model(keyPackageFromDb).Where("installation_id = ?", installationId).Scan(ctx)) - require.Equal(t, installationId, keyPackageFromDb.InstallationId) + keyPackageFromDB := &KeyPackage{} + require.NoError(t, store.db.NewSelect().Model(keyPackageFromDB).Where("installation_id = ?", installationId).Scan(ctx)) + require.Equal(t, installationId, keyPackageFromDB.InstallationId) } func TestCreateInstallationIdempotent(t *testing.T) { diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go index f0066933..375429b4 100644 --- a/pkg/mlsvalidate/service.go +++ b/pkg/mlsvalidate/service.go @@ -23,26 +23,26 @@ type IdentityInput struct { Identity []byte } -type MlsValidationService interface { +type MLSValidationService interface { ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]IdentityValidationResult, error) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]GroupMessageValidationResult, error) } -type MlsValidationServiceImpl struct { +type MLSValidationServiceImpl struct { grpcClient svc.ValidationApiClient } -func NewMlsValidationService(ctx context.Context, options MlsValidationOptions) (*MlsValidationServiceImpl, error) { +func NewMlsValidationService(ctx context.Context, options MlsValidationOptions) (*MLSValidationServiceImpl, error) { conn, err := grpc.DialContext(ctx, options.GrpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } - return &MlsValidationServiceImpl{ + return &MLSValidationServiceImpl{ grpcClient: svc.NewValidationApiClient(conn), }, nil } -func (s *MlsValidationServiceImpl) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]IdentityValidationResult, error) { +func (s *MLSValidationServiceImpl) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]IdentityValidationResult, error) { req := makeValidateKeyPackageRequest(keyPackages) response, err := s.grpcClient.ValidateKeyPackages(ctx, req) if err != nil { @@ -73,7 +73,7 @@ func makeValidateKeyPackageRequest(keyPackageBytes [][]byte) *svc.ValidateKeyPac } } -func (s *MlsValidationServiceImpl) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]GroupMessageValidationResult, error) { +func (s *MLSValidationServiceImpl) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]GroupMessageValidationResult, error) { req := makeValidateGroupMessagesRequest(groupMessages) response, err := s.grpcClient.ValidateGroupMessages(ctx, req) diff --git a/pkg/mlsvalidate/service_test.go b/pkg/mlsvalidate/service_test.go index d78b3367..2fb2cca3 100644 --- a/pkg/mlsvalidate/service_test.go +++ b/pkg/mlsvalidate/service_test.go @@ -10,25 +10,25 @@ import ( "google.golang.org/grpc" ) -type MockedGrpcService struct { +type MockedGRPCService struct { mock.Mock } -func (m *MockedGrpcService) ValidateKeyPackages(ctx context.Context, req *svc.ValidateKeyPackagesRequest, opts ...grpc.CallOption) (*svc.ValidateKeyPackagesResponse, error) { +func (m *MockedGRPCService) ValidateKeyPackages(ctx context.Context, req *svc.ValidateKeyPackagesRequest, opts ...grpc.CallOption) (*svc.ValidateKeyPackagesResponse, error) { args := m.Called(ctx, req) return args.Get(0).(*svc.ValidateKeyPackagesResponse), args.Error(1) } -func (m *MockedGrpcService) ValidateGroupMessages(ctx context.Context, req *svc.ValidateGroupMessagesRequest, opts ...grpc.CallOption) (*svc.ValidateGroupMessagesResponse, error) { +func (m *MockedGRPCService) ValidateGroupMessages(ctx context.Context, req *svc.ValidateGroupMessagesRequest, opts ...grpc.CallOption) (*svc.ValidateGroupMessagesResponse, error) { args := m.Called(ctx, req) return args.Get(0).(*svc.ValidateGroupMessagesResponse), args.Error(1) } -func getMockedService() (*MockedGrpcService, MlsValidationService) { - mockService := new(MockedGrpcService) - service := &MlsValidationServiceImpl{ +func getMockedService() (*MockedGRPCService, MLSValidationService) { + mockService := new(MockedGRPCService) + service := &MLSValidationServiceImpl{ grpcClient: mockService, } diff --git a/pkg/server/server.go b/pkg/server/server.go index 75747414..ab3c1bd1 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -61,7 +61,7 @@ type Server struct { allowLister authz.WalletAllowLister authenticator *authn.XmtpAuthentication grpc *api.Server - mlsStore *mlsstore.Store + MLSStore *mlsstore.Store } // Create a new Server @@ -229,7 +229,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } s.log.With(logging.MultiAddrs("listen", maddrs...)).Info("got server") - var mlsStore mlsstore.MlsStore + var MLSStore mlsstore.MlsStore if options.MlsStore.DbConnectionString != "" { mlsDb, err := createBunDB(options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.MlsStore.MaxOpenConns) @@ -237,7 +237,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) return nil, errors.Wrap(err, "creating mls db") } - s.mlsStore, err = mlsstore.New(s.ctx, mlsstore.Config{ + s.MLSStore, err = mlsstore.New(s.ctx, mlsstore.Config{ Log: s.log, DB: mlsDb, }) @@ -247,9 +247,9 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } } - var mlsValidator mlsvalidate.MlsValidationService + var MLSValidator mlsvalidate.MLSValidationService if options.MlsValidation.GrpcAddress != "" { - mlsValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MlsValidation) + MLSValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MlsValidation) if err != nil { return nil, errors.Wrap(err, "creating mls validation service") } @@ -263,9 +263,9 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) Log: s.log.Named("`api"), Waku: s.wakuNode, Store: s.store, - MlsStore: mlsStore, + MLSStore: MLSStore, AllowLister: s.allowLister, - MlsValidator: mlsValidator, + MLSValidator: MLSValidator, }, ) if err != nil { @@ -306,8 +306,8 @@ func (s *Server) Shutdown() { if s.store != nil { s.store.Close() } - if s.mlsStore != nil { - s.mlsStore.Close() + if s.MLSStore != nil { + s.MLSStore.Close() } // Close metrics server. From e09fca128522365ac53779b1de810e4407ed4f97 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:45:40 -0700 Subject: [PATCH 12/33] Change casing of server options --- cmd/xmtpd/main.go | 4 ++-- pkg/server/options.go | 2 +- pkg/server/server.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/xmtpd/main.go b/cmd/xmtpd/main.go index 69815c1b..c26364a5 100644 --- a/cmd/xmtpd/main.go +++ b/cmd/xmtpd/main.go @@ -92,8 +92,8 @@ func main() { return } - if options.CreateMlsMigration != "" && options.MlsStore.DbConnectionString != "" { - if err := server.CreateMlsMigration(options.CreateMlsMigration, options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.Store.MaxOpenConns); err != nil { + if options.CreateMlsMigration != "" && options.MLSStore.DbConnectionString != "" { + if err := server.CreateMlsMigration(options.CreateMlsMigration, options.MLSStore.DbConnectionString, options.WaitForDB, options.MLSStore.ReadTimeout, options.MLSStore.WriteTimeout, options.Store.MaxOpenConns); err != nil { log.Fatal("creating authz db migration", zap.Error(err)) } return diff --git a/pkg/server/options.go b/pkg/server/options.go index aabfffba..69096d9c 100644 --- a/pkg/server/options.go +++ b/pkg/server/options.go @@ -76,6 +76,6 @@ type Options struct { Metrics MetricsOptions `group:"Metrics Options"` Tracing TracingOptions `group:"DD APM Tracing Options"` Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` - MlsStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mls-store"` + MLSStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mls-store"` MlsValidation mlsvalidate.MlsValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` } diff --git a/pkg/server/server.go b/pkg/server/server.go index ab3c1bd1..f720e04d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -231,8 +231,8 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) var MLSStore mlsstore.MlsStore - if options.MlsStore.DbConnectionString != "" { - mlsDb, err := createBunDB(options.MlsStore.DbConnectionString, options.WaitForDB, options.MlsStore.ReadTimeout, options.MlsStore.WriteTimeout, options.MlsStore.MaxOpenConns) + if options.MLSStore.DbConnectionString != "" { + mlsDb, err := createBunDB(options.MLSStore.DbConnectionString, options.WaitForDB, options.MLSStore.ReadTimeout, options.MLSStore.WriteTimeout, options.MLSStore.MaxOpenConns) if err != nil { return nil, errors.Wrap(err, "creating mls db") } From 293053a1e0b33cee2bd6ed52420b74dc4fea638a Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:46:55 -0700 Subject: [PATCH 13/33] Change casing of validation options --- pkg/mlsvalidate/config.go | 4 ++-- pkg/mlsvalidate/service.go | 4 ++-- pkg/server/options.go | 2 +- pkg/server/server.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/mlsvalidate/config.go b/pkg/mlsvalidate/config.go index 895de47f..dd644eab 100644 --- a/pkg/mlsvalidate/config.go +++ b/pkg/mlsvalidate/config.go @@ -1,5 +1,5 @@ package mlsvalidate -type MlsValidationOptions struct { - GrpcAddress string `long:"grpc-address" description:"Address for the GRPC validation service"` +type MLSValidationOptions struct { + GRPCAddress string `long:"grpc-address" description:"Address for the GRPC validation service"` } diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go index 375429b4..36756ee2 100644 --- a/pkg/mlsvalidate/service.go +++ b/pkg/mlsvalidate/service.go @@ -32,8 +32,8 @@ type MLSValidationServiceImpl struct { grpcClient svc.ValidationApiClient } -func NewMlsValidationService(ctx context.Context, options MlsValidationOptions) (*MLSValidationServiceImpl, error) { - conn, err := grpc.DialContext(ctx, options.GrpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) +func NewMlsValidationService(ctx context.Context, options MLSValidationOptions) (*MLSValidationServiceImpl, error) { + conn, err := grpc.DialContext(ctx, options.GRPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } diff --git a/pkg/server/options.go b/pkg/server/options.go index 69096d9c..e21e3efb 100644 --- a/pkg/server/options.go +++ b/pkg/server/options.go @@ -77,5 +77,5 @@ type Options struct { Tracing TracingOptions `group:"DD APM Tracing Options"` Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` MLSStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mls-store"` - MlsValidation mlsvalidate.MlsValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` + MlsValidation mlsvalidate.MLSValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` } diff --git a/pkg/server/server.go b/pkg/server/server.go index f720e04d..8887fb82 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -248,7 +248,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } var MLSValidator mlsvalidate.MLSValidationService - if options.MlsValidation.GrpcAddress != "" { + if options.MlsValidation.GRPCAddress != "" { MLSValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MlsValidation) if err != nil { return nil, errors.Wrap(err, "creating mls validation service") From 8849ad5ba3df3e7f46267583c694270d0d6196d3 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 08:47:22 -0700 Subject: [PATCH 14/33] Remove unused function --- pkg/api/message/v3/service.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index c0a64def..1cb45038 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -137,13 +137,3 @@ func (s *Service) RevokeInstallation(ctx context.Context, req *proto.RevokeInsta func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentityUpdatesRequest) (*proto.GetIdentityUpdatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "unimplemented") } - -func indexOf(target string, ids []string) int { - for i, id := range ids { - if id == target { - return i - } - } - - return -1 -} From 17ea986bb78d760d2e423f659c23c4161354f443 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:04:32 -0700 Subject: [PATCH 15/33] Remove double pointer --- pkg/mlsstore/store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 3d539338..f528b78d 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -69,7 +69,7 @@ func (s *Store) CreateInstallation(ctx context.Context, installationId string, w } _, err = tx.NewInsert(). - Model(&keyPackage). + Model(keyPackage). Ignore(). Exec(ctx) From b8b8b4d9bfe8c7f999cfaad3949821ed16f08532 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:05:21 -0700 Subject: [PATCH 16/33] Make private again --- pkg/api/message/v3/service.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 1cb45038..9ea61de4 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -20,7 +20,7 @@ type Service struct { log *zap.Logger waku *wakunode.WakuNode messageStore *store.Store - MLSStore mlsstore.MlsStore + mlsStore mlsstore.MlsStore validationService mlsvalidate.MLSValidationService ctx context.Context @@ -32,7 +32,7 @@ func NewService(node *wakunode.WakuNode, logger *zap.Logger, messageStore *store log: logger.Named("message/v3"), waku: node, messageStore: messageStore, - MLSStore: mlsStore, + mlsStore: mlsStore, validationService: validationService, } @@ -59,7 +59,7 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI installationId := results[0].InstallationId walletAddress := results[0].WalletAddress - err = s.MLSStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) + err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { ids := req.InstallationIds - keyPackages, err := s.MLSStore.ConsumeKeyPackages(ctx, ids) + keyPackages, err := s.mlsStore.ConsumeKeyPackages(ctx, ids) if err != nil { return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) } @@ -122,7 +122,7 @@ func (s *Service) UploadKeyPackages(ctx context.Context, req *proto.UploadKeyPac kp := mlsstore.NewKeyPackage(validationResult.InstallationId, keyPackageBytes[i], false) keyPackageModels[i] = kp } - err = s.MLSStore.InsertKeyPackages(ctx, keyPackageModels) + err = s.mlsStore.InsertKeyPackages(ctx, keyPackageModels) if err != nil { return nil, status.Errorf(codes.Internal, "failed to insert key packages: %s", err) } From 08266da2e5e02b52e56d8f56dd693be9254906b9 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:07:14 -0700 Subject: [PATCH 17/33] Fix pointer to key package --- pkg/mlsstore/store.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index d2ce3c28..be154f45 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -201,8 +201,8 @@ func (s *Store) RevokeInstallation(ctx context.Context, installationId string) e return err } -func NewKeyPackage(installationId string, data []byte, isLastResort bool) KeyPackage { - return KeyPackage{ +func NewKeyPackage(installationId string, data []byte, isLastResort bool) *KeyPackage { + return &KeyPackage{ ID: buildKeyPackageId(data), InstallationId: installationId, CreatedAt: nowNs(), From 0ba529118e59759c5b01f5f04fc5d198db00ac0e Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:08:27 -0700 Subject: [PATCH 18/33] Capitalize more things --- pkg/api/message/v3/service_test.go | 2 +- pkg/mlsstore/store_test.go | 2 +- pkg/server/server.go | 4 ++-- pkg/testing/store.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index a0b92075..80755c01 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -57,7 +57,7 @@ func (m *mockedMlsValidationService) mockValidateGroupMessages(groupId string) * func newTestService(t *testing.T, ctx context.Context) (*Service, *bun.DB, *mockedMlsValidationService, func()) { log := test.NewLog(t) - mlsDb, _, mlsDbCleanup := test.NewMlsDB(t) + mlsDb, _, mlsDbCleanup := test.NewMLSDB(t) mlsStore, err := mlsstore.New(ctx, mlsstore.Config{ Log: log, DB: mlsDb, diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index dad0349c..a2b1a10b 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -10,7 +10,7 @@ import ( func NewTestStore(t *testing.T) (*Store, func()) { log := test.NewLog(t) - db, _, dbCleanup := test.NewMlsDB(t) + db, _, dbCleanup := test.NewMLSDB(t) ctx := context.Background() c := Config{ Log: log, diff --git a/pkg/server/server.go b/pkg/server/server.go index 8113de4b..9e481a78 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -248,8 +248,8 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } var MLSValidator mlsvalidate.MLSValidationService - if options.MlsValidation.GRPCAddress != "" { - MLSValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MlsValidation) + if options.MLSValidation.GRPCAddress != "" { + MLSValidator, err = mlsvalidate.NewMlsValidationService(ctx, options.MLSValidation) if err != nil { return nil, errors.Wrap(err, "creating mls validation service") } diff --git a/pkg/testing/store.go b/pkg/testing/store.go index f9a2266c..3727fe27 100644 --- a/pkg/testing/store.go +++ b/pkg/testing/store.go @@ -50,7 +50,7 @@ func NewAuthzDB(t *testing.T) (*bun.DB, string, func()) { return bunDB, dsn, cleanup } -func NewMlsDB(t *testing.T) (*bun.DB, string, func()) { +func NewMLSDB(t *testing.T) (*bun.DB, string, func()) { db, dsn, cleanup := NewDB(t) bunDB := bun.NewDB(db, pgdialect.New()) From 1e681704feb19f8fc7707af12a039a2450b57bfa Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:15:13 -0700 Subject: [PATCH 19/33] Update server fields --- pkg/server/server.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pkg/server/server.go b/pkg/server/server.go index 9e481a78..90f96e12 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -61,7 +61,7 @@ type Server struct { allowLister authz.WalletAllowLister authenticator *authn.XmtpAuthentication grpc *api.Server - MLSStore *mlsstore.Store + mlsDB *bun.DB } // Create a new Server @@ -229,20 +229,17 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) } s.log.With(logging.MultiAddrs("listen", maddrs...)).Info("got server") + var MLSStore *mlsstore.Store if options.MLSStore.DbConnectionString != "" { - s.log.Info("creating mls db") - mlsDb, err := createBunDB(options.MLSStore.DbConnectionString, options.WaitForDB, options.MLSStore.ReadTimeout, options.MLSStore.WriteTimeout, options.MLSStore.MaxOpenConns) - if err != nil { + if s.mlsDB, err = createBunDB(options.MLSStore.DbConnectionString, options.WaitForDB, options.MLSStore.ReadTimeout, options.MLSStore.WriteTimeout, options.MLSStore.MaxOpenConns); err != nil { return nil, errors.Wrap(err, "creating mls db") } s.log.Info("creating mls store") - s.MLSStore, err = mlsstore.New(s.ctx, mlsstore.Config{ + if MLSStore, err = mlsstore.New(s.ctx, mlsstore.Config{ Log: s.log, - DB: mlsDb, - }) - - if err != nil { + DB: s.mlsDB, + }); err != nil { return nil, errors.Wrap(err, "creating mls store") } } @@ -263,7 +260,7 @@ func New(ctx context.Context, log *zap.Logger, options Options) (*Server, error) Log: s.log.Named("`api"), Waku: s.wakuNode, Store: s.store, - MLSStore: s.MLSStore, + MLSStore: MLSStore, AllowLister: s.allowLister, MLSValidator: MLSValidator, }, @@ -306,8 +303,8 @@ func (s *Server) Shutdown() { if s.store != nil { s.store.Close() } - if s.MLSStore != nil { - s.MLSStore.Close() + if s.mlsDB != nil { + s.mlsDB.Close() } // Close metrics server. From 911a0db4de343e7c79381312de1f6a9cb0d90d72 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:25:44 -0700 Subject: [PATCH 20/33] Add test for sort methods --- pkg/mlsstore/store.go | 6 ------ pkg/mlsstore/store_test.go | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index be154f45..4f44c416 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -41,12 +41,6 @@ func New(ctx context.Context, config Config) (*Store, error) { return s, nil } -func (s *Store) Close() { - if s.db != nil { - s.db.Close() - } -} - // Creates the installation and last resort key package func (s *Store) CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error { createdAt := nowNs() diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index a2b1a10b..a1fa3eec 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -2,6 +2,7 @@ package mlsstore import ( "context" + "sort" "testing" "github.com/stretchr/testify/require" @@ -226,3 +227,24 @@ func TestGetIdentityUpdatesNoResult(t *testing.T) { require.NoError(t, err) require.Len(t, identityUpdates[walletAddress], 0) } + +func TestIdentityUpdateSort(t *testing.T) { + updates := IdentityUpdateList([]IdentityUpdate{ + { + Kind: Create, + TimestampNs: 2, + }, + { + Kind: Create, + TimestampNs: 3, + }, + { + Kind: Create, + TimestampNs: 1, + }, + }) + sort.Sort(updates) + require.Equal(t, updates[0].TimestampNs, uint64(1)) + require.Equal(t, updates[1].TimestampNs, uint64(2)) + require.Equal(t, updates[2].TimestampNs, uint64(3)) +} From d0422ba347d650329ed13671771e36380e97879c Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:26:41 -0700 Subject: [PATCH 21/33] Save change to capitalization --- pkg/server/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/options.go b/pkg/server/options.go index e21e3efb..477c99e9 100644 --- a/pkg/server/options.go +++ b/pkg/server/options.go @@ -77,5 +77,5 @@ type Options struct { Tracing TracingOptions `group:"DD APM Tracing Options"` Profiling ProfilingOptions `group:"DD APM Profiling Options" namespace:"profiling"` MLSStore mlsstore.StoreOptions `group:"MLS Options" namespace:"mls-store"` - MlsValidation mlsvalidate.MLSValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` + MLSValidation mlsvalidate.MLSValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` } From a50e30d65658e33b8fa3a2a7997bdcd8daff0fee Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:30:37 -0700 Subject: [PATCH 22/33] Fix lint warnings --- pkg/api/message/v3/service.go | 2 +- pkg/api/message/v3/service_test.go | 2 ++ pkg/store/query_test.go | 1 + pkg/testing/random.go | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 1afbbdb8..a729d24d 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -265,7 +265,7 @@ func validatePublishWelcomesRequest(req *proto.PublishWelcomesRequest) error { return status.Errorf(codes.InvalidArgument, "invalid welcome message") } ciphertext := welcome.WelcomeMessage.GetV1().Ciphertext - if ciphertext == nil || len(ciphertext) == 0 { + if len(ciphertext) == 0 { return status.Errorf(codes.InvalidArgument, "invalid welcome message") } } diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index 80755c01..10da71da 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -70,10 +70,12 @@ func newTestService(t *testing.T, ctx context.Context) (*Service, *bun.DB, *mock ReaderDB: messageDb, CleanerDB: messageDb, }) + require.NoError(t, err) node, nodeCleanup := test.NewNode(t) mlsValidationService := newMockedValidationService() svc, err := NewService(node, log, messageStore, mlsStore, mlsValidationService) + require.NoError(t, err) return svc, mlsDb, mlsValidationService, func() { messageStore.Close() diff --git a/pkg/store/query_test.go b/pkg/store/query_test.go index afd087cf..0a57e96e 100644 --- a/pkg/store/query_test.go +++ b/pkg/store/query_test.go @@ -303,6 +303,7 @@ func TestMlsMessagePublish(t *testing.T) { response, err := store.Query(&messagev1.QueryRequest{ ContentTopics: []string{contentTopic}, }) + require.NoError(t, err) require.Len(t, response.Envelopes, 1) require.Equal(t, response.Envelopes[0].Message, message) require.Equal(t, response.Envelopes[0].ContentTopic, contentTopic) diff --git a/pkg/testing/random.go b/pkg/testing/random.go index 3d0d4e9c..508331db 100644 --- a/pkg/testing/random.go +++ b/pkg/testing/random.go @@ -1,6 +1,7 @@ package testing import ( + cryptoRand "crypto/rand" "math/rand" "strings" ) @@ -21,6 +22,6 @@ func RandomStringLower(n int) string { func RandomBytes(n int) []byte { b := make([]byte, n) - _, _ = rand.Read(b) + _, _ = cryptoRand.Read(b) return b } From 24c8c2a80e0ffdbdbeee669306eafe549ed34891 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:42:21 -0700 Subject: [PATCH 23/33] Fix problem with mocks --- pkg/api/message/v3/service_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index 10da71da..9bc9bef0 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -25,7 +25,12 @@ type mockedMlsValidationService struct { func (m *mockedMlsValidationService) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]mlsvalidate.IdentityValidationResult, error) { args := m.Called(ctx, keyPackages) - return args.Get(0).([]mlsvalidate.IdentityValidationResult), args.Error(1) + response := args.Get(0) + if response == nil { + return nil, args.Error(1) + } + + return response.([]mlsvalidate.IdentityValidationResult), args.Error(1) } func (m *mockedMlsValidationService) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]mlsvalidate.GroupMessageValidationResult, error) { From bd821d9d60cfdeecbe5541a582e8692eea586d12 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:09:28 -0700 Subject: [PATCH 24/33] Fix index name --- pkg/migrations/mls/20231023050806_init-schema.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/migrations/mls/20231023050806_init-schema.up.sql b/pkg/migrations/mls/20231023050806_init-schema.up.sql index b6aa90d5..7b2c54ca 100644 --- a/pkg/migrations/mls/20231023050806_init-schema.up.sql +++ b/pkg/migrations/mls/20231023050806_init-schema.up.sql @@ -33,7 +33,7 @@ CREATE INDEX idx_installations_revoked_at ON installations(revoked_at); --bun:split -- Adding indexes for the key_packages table -CREATE INDEX idx_key_packages_installation_id_not_is_last_resort_created_at ON key_packages( +CREATE INDEX idx_key_packages_installation_id_not_consumed_is_last_resort_created_at ON key_packages( installation_id, not_consumed, is_last_resort, From 6b576914c88aeaf4c0f87f3f68f8e23c818e5efb Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:15:17 -0700 Subject: [PATCH 25/33] Move sorting to the store --- pkg/api/message/v3/service.go | 3 +- pkg/mlsstore/store.go | 63 +++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index a729d24d..6a787503 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -2,7 +2,6 @@ package api import ( "context" - "sort" wakunode "github.com/waku-org/go-waku/waku/v2/node" wakupb "github.com/waku-org/go-waku/waku/v2/protocol/pb" @@ -212,7 +211,7 @@ func (s *Service) GetIdentityUpdates(ctx context.Context, req *proto.GetIdentity resUpdates := make([]*proto.GetIdentityUpdatesResponse_WalletUpdates, len(walletAddresses)) for i, walletAddress := range walletAddresses { walletUpdates := updates[walletAddress] - sort.Sort(walletUpdates) + resUpdates[i] = &proto.GetIdentityUpdatesResponse_WalletUpdates{ Updates: []*proto.GetIdentityUpdatesResponse_Update{}, } diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 4f44c416..7f05c5e5 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -6,6 +6,7 @@ import ( "database/sql" "encoding/hex" "errors" + "sort" "time" "github.com/uptrace/bun" @@ -120,36 +121,9 @@ func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string return keyPackages, nil } -type IdentityUpdateKind int - -const ( - Create IdentityUpdateKind = iota - Revoke -) - -type IdentityUpdate struct { - Kind IdentityUpdateKind - InstallationId string - TimestampNs uint64 -} - -// Add the required methods to make a valid sort.Sort interface -type IdentityUpdateList []IdentityUpdate - -func (a IdentityUpdateList) Len() int { - return len(a) -} - -func (a IdentityUpdateList) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a IdentityUpdateList) Less(i, j int) bool { - return a[i].TimestampNs < a[j].TimestampNs -} - func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) { updated := make([]*Installation, 0) + // Find all installations that were changed since the startTimeNs err := s.db.NewSelect(). Model(&updated). Where("wallet_address IN (?)", bun.In(walletAddresses)). @@ -163,6 +137,7 @@ func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string return nil, err } + // The returned list is only partially sorted out := make(map[string]IdentityUpdateList) for _, installation := range updated { if installation.CreatedAt > startTimeNs { @@ -180,6 +155,10 @@ func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string }) } } + // Sort the updates by timestamp now that the full list is assembled + for _, updates := range out { + sort.Sort(updates) + } return out, nil } @@ -241,3 +220,31 @@ func buildKeyPackageId(keyPackageData []byte) string { digest := sha256.Sum256(keyPackageData) return hex.EncodeToString(digest[:]) } + +type IdentityUpdateKind int + +const ( + Create IdentityUpdateKind = iota + Revoke +) + +type IdentityUpdate struct { + Kind IdentityUpdateKind + InstallationId string + TimestampNs uint64 +} + +// Add the required methods to make a valid sort.Sort interface +type IdentityUpdateList []IdentityUpdate + +func (a IdentityUpdateList) Len() int { + return len(a) +} + +func (a IdentityUpdateList) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a IdentityUpdateList) Less(i, j int) bool { + return a[i].TimestampNs < a[j].TimestampNs +} From cd38308802e290891ce7d7e07683fec9aa64d5e3 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:40:00 -0700 Subject: [PATCH 26/33] Fix ciphertext validation --- pkg/api/message/v3/service.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 6a787503..cf388f63 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -263,8 +263,9 @@ func validatePublishWelcomesRequest(req *proto.PublishWelcomesRequest) error { if welcome == nil || welcome.WelcomeMessage == nil { return status.Errorf(codes.InvalidArgument, "invalid welcome message") } - ciphertext := welcome.WelcomeMessage.GetV1().Ciphertext - if len(ciphertext) == 0 { + + v1 := welcome.WelcomeMessage.GetV1() + if v1 == nil || len(v1.Ciphertext) == 0 { return status.Errorf(codes.InvalidArgument, "invalid welcome message") } } From cfffd9bd6c9488d8b54ff6fbdab90b36328e7931 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:06:47 -0700 Subject: [PATCH 27/33] Make installation_id bytes --- go.mod | 2 +- go.sum | 4 ++ pkg/api/message/v3/service.go | 12 ++--- pkg/api/message/v3/service_test.go | 39 ++++++++-------- .../mls/20231023050806_init-schema.up.sql | 5 ++- pkg/mlsstore/models.go | 37 +++++++++++----- pkg/mlsstore/store.go | 26 ++++++----- pkg/mlsstore/store_test.go | 44 +++++++++---------- pkg/mlsvalidate/service.go | 10 +++-- pkg/mlsvalidate/service_test.go | 12 ++--- pkg/store/query_test.go | 2 +- pkg/store/store.go | 2 +- pkg/topic/topic.go | 4 +- 13 files changed, 113 insertions(+), 86 deletions(-) diff --git a/go.mod b/go.mod index e86b30f8..1481fb76 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/uptrace/bun/driver/pgdriver v1.1.16 github.com/waku-org/go-waku v0.8.0 github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 - github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4 + github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab github.com/yoheimuta/protolint v0.39.0 go.uber.org/zap v1.24.0 golang.org/x/sync v0.3.0 diff --git a/go.sum b/go.sum index 506a1663..9e9fac51 100644 --- a/go.sum +++ b/go.sum @@ -1158,6 +1158,10 @@ github.com/xmtp/proto/v3 v3.29.1-0.20231019225839-328520e94f34 h1:rR10cJ5RTlw7OW github.com/xmtp/proto/v3 v3.29.1-0.20231019225839-328520e94f34/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4 h1:Qc2ed8NrlosJnPMNxVriugcFB21d4V90HKZdO83yV2M= github.com/xmtp/proto/v3 v3.29.1-0.20231023182354-832c8d572ed4/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231025193535-9760f07c3401 h1:ctStMkU5570kEgpVnsklo7PUOuTridm8bkXcZ1i6sXI= +github.com/xmtp/proto/v3 v3.29.1-0.20231025193535-9760f07c3401/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab h1:hWBftgxB7QWXDOOv1Wah6VZ6mwSFZX8e8rEGJQHm8zA= +github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/yoheimuta/go-protoparser/v4 v4.6.0 h1:uvz1e9/5Ihsm4Ku8AJeDImTpirKmIxubZdSn0QJNdnw= github.com/yoheimuta/go-protoparser/v4 v4.6.0/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8= diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index cf388f63..3f79bdec 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -66,20 +66,20 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI } func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { - ids := req.InstallationIds + ids := mlsstore.InstallationIdArray(req.InstallationIds) keyPackages, err := s.mlsStore.ConsumeKeyPackages(ctx, ids) if err != nil { return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) } keyPackageMap := make(map[string]int) for idx, id := range ids { - keyPackageMap[id] = idx + keyPackageMap[id.String()] = idx } resPackages := make([]*proto.ConsumeKeyPackagesResponse_KeyPackage, len(keyPackages)) for _, keyPackage := range keyPackages { - idx, ok := keyPackageMap[keyPackage.InstallationId] + idx, ok := keyPackageMap[keyPackage.InstallationId.String()] if !ok { return nil, status.Errorf(codes.Internal, "could not find key package for installation") } @@ -117,7 +117,7 @@ func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupR for i, result := range validationResults { message := messages[i] - if err = isReadyToSend(result.GroupId, message); err != nil { + if err = requireReadyToSend(result.GroupId, message); err != nil { return nil, err } @@ -132,7 +132,7 @@ func (s *Service) PublishToGroup(ctx context.Context, req *proto.PublishToGroupR func (s *Service) publishMessage(ctx context.Context, contentTopic string, message []byte) error { log := s.log.Named("publish-mls").With(zap.String("content_topic", contentTopic)) - env, err := s.messageStore.InsertMlsMessage(ctx, contentTopic, message) + env, err := s.messageStore.InsertMLSMessage(ctx, contentTopic, message) if err != nil { return status.Errorf(codes.Internal, "failed to insert message: %s", err) } @@ -293,7 +293,7 @@ func validateGetIdentityUpdatesRequest(req *proto.GetIdentityUpdatesRequest) err return nil } -func isReadyToSend(groupId string, message []byte) error { +func requireReadyToSend(groupId string, message []byte) error { if groupId == "" { return status.Errorf(codes.InvalidArgument, "group id is empty") } diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index 9bc9bef0..c353f617 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -18,11 +18,11 @@ import ( test "github.com/xmtp/xmtp-node-go/pkg/testing" ) -type mockedMlsValidationService struct { +type mockedMLSValidationService struct { mock.Mock } -func (m *mockedMlsValidationService) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]mlsvalidate.IdentityValidationResult, error) { +func (m *mockedMLSValidationService) ValidateKeyPackages(ctx context.Context, keyPackages [][]byte) ([]mlsvalidate.IdentityValidationResult, error) { args := m.Called(ctx, keyPackages) response := args.Get(0) @@ -33,26 +33,27 @@ func (m *mockedMlsValidationService) ValidateKeyPackages(ctx context.Context, ke return response.([]mlsvalidate.IdentityValidationResult), args.Error(1) } -func (m *mockedMlsValidationService) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]mlsvalidate.GroupMessageValidationResult, error) { +func (m *mockedMLSValidationService) ValidateGroupMessages(ctx context.Context, groupMessages [][]byte) ([]mlsvalidate.GroupMessageValidationResult, error) { args := m.Called(ctx, groupMessages) return args.Get(0).([]mlsvalidate.GroupMessageValidationResult), args.Error(1) } -func newMockedValidationService() *mockedMlsValidationService { - return new(mockedMlsValidationService) +func newMockedValidationService() *mockedMLSValidationService { + return new(mockedMLSValidationService) } -func (m *mockedMlsValidationService) mockValidateKeyPackages(installationId, walletAddress string) *mock.Call { +func (m *mockedMLSValidationService) mockValidateKeyPackages(installationId mlsstore.InstallationId, walletAddress string) *mock.Call { return m.On("ValidateKeyPackages", mock.Anything, mock.Anything).Return([]mlsvalidate.IdentityValidationResult{ { - InstallationId: installationId, - WalletAddress: walletAddress, + InstallationId: installationId, + WalletAddress: walletAddress, + CredentialIdentity: []byte("test"), }, }, nil) } -func (m *mockedMlsValidationService) mockValidateGroupMessages(groupId string) *mock.Call { +func (m *mockedMLSValidationService) mockValidateGroupMessages(groupId string) *mock.Call { return m.On("ValidateGroupMessages", mock.Anything, mock.Anything).Return([]mlsvalidate.GroupMessageValidationResult{ { GroupId: groupId, @@ -60,7 +61,7 @@ func (m *mockedMlsValidationService) mockValidateGroupMessages(groupId string) * }, nil) } -func newTestService(t *testing.T, ctx context.Context) (*Service, *bun.DB, *mockedMlsValidationService, func()) { +func newTestService(t *testing.T, ctx context.Context) (*Service, *bun.DB, *mockedMLSValidationService, func()) { log := test.NewLog(t) mlsDb, _, mlsDbCleanup := test.NewMLSDB(t) mlsStore, err := mlsstore.New(ctx, mlsstore.Config{ @@ -95,7 +96,7 @@ func TestRegisterInstallation(t *testing.T) { svc, mlsDb, mlsValidationService, cleanup := newTestService(t, ctx) defer cleanup() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) @@ -138,7 +139,7 @@ func TestUploadKeyPackages(t *testing.T) { svc, mlsDb, mlsValidationService, cleanup := newTestService(t, ctx) defer cleanup() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) @@ -170,7 +171,7 @@ func TestConsumeKeyPackages(t *testing.T) { svc, _, mlsValidationService, cleanup := newTestService(t, ctx) defer cleanup() - installationId1 := test.RandomString(32) + installationId1 := test.RandomBytes(32) walletAddress1 := test.RandomString(32) mockCall := mlsValidationService.mockValidateKeyPackages(installationId1, walletAddress1) @@ -184,7 +185,7 @@ func TestConsumeKeyPackages(t *testing.T) { require.NotNil(t, res) // Add a second key package - installationId2 := test.RandomString(32) + installationId2 := test.RandomBytes(32) walletAddress2 := test.RandomString(32) // Unset the original mock so we can set a new one mockCall.Unset() @@ -199,7 +200,7 @@ func TestConsumeKeyPackages(t *testing.T) { require.NotNil(t, res) consumeRes, err := svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ - InstallationIds: []string{installationId1, installationId2}, + InstallationIds: [][]byte{installationId1, installationId2}, }) require.NoError(t, err) require.NotNil(t, consumeRes) @@ -209,7 +210,7 @@ func TestConsumeKeyPackages(t *testing.T) { // Now do it with the installationIds reversed consumeRes, err = svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ - InstallationIds: []string{installationId2, installationId1}, + InstallationIds: [][]byte{installationId2, installationId1}, }) require.NoError(t, err) @@ -226,7 +227,7 @@ func TestConsumeKeyPackagesFail(t *testing.T) { defer cleanup() consumeRes, err := svc.ConsumeKeyPackages(ctx, &proto.ConsumeKeyPackagesRequest{ - InstallationIds: []string{test.RandomString(32)}, + InstallationIds: [][]byte{test.RandomBytes(32)}, }) require.Error(t, err) require.Nil(t, consumeRes) @@ -266,7 +267,7 @@ func TestGetIdentityUpdates(t *testing.T) { svc, _, mlsValidationService, cleanup := newTestService(t, ctx) defer cleanup() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) mockCall := mlsValidationService.mockValidateKeyPackages(installationId, walletAddress) @@ -292,7 +293,7 @@ func TestGetIdentityUpdates(t *testing.T) { } mockCall.Unset() - mlsValidationService.mockValidateKeyPackages(test.RandomString(32), walletAddress) + mlsValidationService.mockValidateKeyPackages(test.RandomBytes(32), walletAddress) _, err = svc.RegisterInstallation(ctx, &proto.RegisterInstallationRequest{ LastResortKeyPackage: &proto.KeyPackageUpload{ KeyPackageTlsSerialized: []byte("test"), diff --git a/pkg/migrations/mls/20231023050806_init-schema.up.sql b/pkg/migrations/mls/20231023050806_init-schema.up.sql index 7b2c54ca..e77152e9 100644 --- a/pkg/migrations/mls/20231023050806_init-schema.up.sql +++ b/pkg/migrations/mls/20231023050806_init-schema.up.sql @@ -3,16 +3,17 @@ SET --bun:split CREATE TABLE installations ( - id TEXT PRIMARY KEY, + id BYTEA PRIMARY KEY, wallet_address TEXT NOT NULL, created_at BIGINT NOT NULL, + credential_identity BYTEA NOT NULL, revoked_at BIGINT ); --bun:split CREATE TABLE key_packages ( id TEXT PRIMARY KEY, - installation_id TEXT NOT NULL, + installation_id BYTEA NOT NULL, created_at BIGINT NOT NULL, consumed_at BIGINT, not_consumed BOOLEAN DEFAULT TRUE NOT NULL, diff --git a/pkg/mlsstore/models.go b/pkg/mlsstore/models.go index 533c595d..216d0e8f 100644 --- a/pkg/mlsstore/models.go +++ b/pkg/mlsstore/models.go @@ -2,23 +2,38 @@ package mlsstore import "github.com/uptrace/bun" +type InstallationId []byte + +func (id InstallationId) String() string { + return string(id) +} + type Installation struct { bun.BaseModel `bun:"table:installations"` - ID string `bun:",pk"` - WalletAddress string `bun:"wallet_address,notnull"` - CreatedAt int64 `bun:"created_at,notnull"` - RevokedAt *int64 `bun:"revoked_at"` + ID InstallationId `bun:",pk,type:bytea"` + WalletAddress string `bun:"wallet_address,notnull"` + CreatedAt int64 `bun:"created_at,notnull"` + RevokedAt *int64 `bun:"revoked_at"` + CredentialIdentity []byte `bun:"credential_identity,notnull,type:bytea"` } type KeyPackage struct { bun.BaseModel `bun:"table:key_packages"` - ID string `bun:",pk"` // ID is the hash of the data field - InstallationId string `bun:"installation_id,notnull"` - CreatedAt int64 `bun:"created_at,notnull"` - ConsumedAt *int64 `bun:"consumed_at"` - NotConsumed bool `bun:"not_consumed,default:true"` - IsLastResort bool `bun:"is_last_resort,notnull"` - Data []byte `bun:"data,notnull,type:bytea"` + ID string `bun:",pk"` // ID is the hash of the data field + InstallationId InstallationId `bun:"installation_id,notnull,type:bytea"` + CreatedAt int64 `bun:"created_at,notnull"` + ConsumedAt *int64 `bun:"consumed_at"` + NotConsumed bool `bun:"not_consumed,default:true"` + IsLastResort bool `bun:"is_last_resort,notnull"` + Data []byte `bun:"data,notnull,type:bytea"` +} + +func InstallationIdArray(data [][]byte) []InstallationId { + result := make([]InstallationId, len(data)) + for i, d := range data { + result[i] = InstallationId(d) + } + return result } diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 7f05c5e5..420bc4b3 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -22,9 +22,9 @@ type Store struct { } type MlsStore interface { - CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error + CreateInstallation(ctx context.Context, installationId InstallationId, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage) error - ConsumeKeyPackages(ctx context.Context, installationIds []string) ([]*KeyPackage, error) + ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) } @@ -43,13 +43,14 @@ func New(ctx context.Context, config Config) (*Store, error) { } // Creates the installation and last resort key package -func (s *Store) CreateInstallation(ctx context.Context, installationId string, walletAddress string, lastResortKeyPackage []byte) error { +func (s *Store) CreateInstallation(ctx context.Context, installationId InstallationId, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error { createdAt := nowNs() installation := Installation{ - ID: installationId, - WalletAddress: walletAddress, - CreatedAt: createdAt, + ID: installationId, + WalletAddress: walletAddress, + CreatedAt: createdAt, + CredentialIdentity: credentialIdentity, } keyPackage := NewKeyPackage(installationId, lastResortKeyPackage, true) @@ -83,7 +84,7 @@ func (s *Store) InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage return err } -func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []string) ([]*KeyPackage, error) { +func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) { keyPackages := make([]*KeyPackage, 0) err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { err := tx.NewRaw(` @@ -163,7 +164,7 @@ func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string return out, nil } -func (s *Store) RevokeInstallation(ctx context.Context, installationId string) error { +func (s *Store) RevokeInstallation(ctx context.Context, installationId InstallationId) error { _, err := s.db.NewUpdate(). Model(&Installation{}). Set("revoked_at = ?", nowNs()). @@ -174,7 +175,7 @@ func (s *Store) RevokeInstallation(ctx context.Context, installationId string) e return err } -func NewKeyPackage(installationId string, data []byte, isLastResort bool) *KeyPackage { +func NewKeyPackage(installationId InstallationId, data []byte, isLastResort bool) *KeyPackage { return &KeyPackage{ ID: buildKeyPackageId(data), InstallationId: installationId, @@ -229,9 +230,10 @@ const ( ) type IdentityUpdate struct { - Kind IdentityUpdateKind - InstallationId string - TimestampNs uint64 + Kind IdentityUpdateKind + InstallationId []byte + CredentialIdentity []byte + TimestampNs uint64 } // Add the required methods to make a valid sort.Sort interface diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index a1fa3eec..c610659f 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -29,10 +29,10 @@ func TestCreateInstallation(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32)) + err := store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32), test.RandomBytes(32)) require.NoError(t, err) installationFromDb := &Installation{} @@ -49,13 +49,13 @@ func TestCreateInstallationIdempotent(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage, keyPackage) require.NoError(t, err) - err = store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32)) + err = store.CreateInstallation(ctx, installationId, walletAddress, test.RandomBytes(32), test.RandomBytes(32)) require.NoError(t, err) keyPackageFromDb := &KeyPackage{} @@ -68,11 +68,11 @@ func TestInsertKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage, keyPackage) require.NoError(t, err) keyPackage2 := test.RandomBytes(32) @@ -111,14 +111,14 @@ func TestConsumeLastResortKeyPackage(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage, keyPackage) require.NoError(t, err) - consumeResult, err := store.ConsumeKeyPackages(ctx, []string{installationId}) + consumeResult, err := store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) require.Equal(t, keyPackage, consumeResult[0].Data) @@ -130,11 +130,11 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomString(32) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage) + err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage, keyPackage) require.NoError(t, err) keyPackage2 := test.RandomBytes(32) @@ -146,13 +146,13 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { Data: keyPackage2, }})) - consumeResult, err := store.ConsumeKeyPackages(ctx, []string{installationId}) + consumeResult, err := store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) require.Equal(t, keyPackage2, consumeResult[0].Data) require.Equal(t, installationId, consumeResult[0].InstallationId) - consumeResult, err = store.ConsumeKeyPackages(ctx, []string{installationId}) + consumeResult, err = store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) // Now we are out of regular key packages. Expect to consume the last resort @@ -166,16 +166,16 @@ func TestGetIdentityUpdates(t *testing.T) { ctx := context.Background() walletAddress := test.RandomString(32) - installationId1 := test.RandomString(32) + installationId1 := test.RandomBytes(32) keyPackage1 := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId1, walletAddress, keyPackage1) + err := store.CreateInstallation(ctx, installationId1, walletAddress, keyPackage1, keyPackage1) require.NoError(t, err) - installationId2 := test.RandomString(32) + installationId2 := test.RandomBytes(32) keyPackage2 := test.RandomBytes(32) - err = store.CreateInstallation(ctx, installationId2, walletAddress, keyPackage2) + err = store.CreateInstallation(ctx, installationId2, walletAddress, keyPackage2, keyPackage2) require.NoError(t, err) identityUpdates, err := store.GetIdentityUpdates(ctx, []string{walletAddress}, 0) @@ -197,17 +197,17 @@ func TestGetIdentityUpdatesMultipleWallets(t *testing.T) { ctx := context.Background() walletAddress1 := test.RandomString(32) - installationId1 := test.RandomString(32) + installationId1 := test.RandomBytes(32) keyPackage1 := test.RandomBytes(32) - err := store.CreateInstallation(ctx, installationId1, walletAddress1, keyPackage1) + err := store.CreateInstallation(ctx, installationId1, walletAddress1, keyPackage1, keyPackage1) require.NoError(t, err) walletAddress2 := test.RandomString(32) - installationId2 := test.RandomString(32) + installationId2 := test.RandomBytes(32) keyPackage2 := test.RandomBytes(32) - err = store.CreateInstallation(ctx, installationId2, walletAddress2, keyPackage2) + err = store.CreateInstallation(ctx, installationId2, walletAddress2, keyPackage2, keyPackage2) require.NoError(t, err) identityUpdates, err := store.GetIdentityUpdates(ctx, []string{walletAddress1, walletAddress2}, 0) diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go index 36756ee2..686e533e 100644 --- a/pkg/mlsvalidate/service.go +++ b/pkg/mlsvalidate/service.go @@ -10,8 +10,9 @@ import ( ) type IdentityValidationResult struct { - WalletAddress string - InstallationId string + WalletAddress string + InstallationId []byte + CredentialIdentity []byte } type GroupMessageValidationResult struct { @@ -54,8 +55,9 @@ func (s *MLSValidationServiceImpl) ValidateKeyPackages(ctx context.Context, keyP return nil, fmt.Errorf("validation failed with error %s", response.ErrorMessage) } out[i] = IdentityValidationResult{ - WalletAddress: response.WalletAddress, - InstallationId: response.InstallationId, + WalletAddress: response.WalletAddress, + InstallationId: response.InstallationId, + CredentialIdentity: response.CredentialIdentityBytes, } } return out, nil diff --git a/pkg/mlsvalidate/service_test.go b/pkg/mlsvalidate/service_test.go index 2fb2cca3..9613eb98 100644 --- a/pkg/mlsvalidate/service_test.go +++ b/pkg/mlsvalidate/service_test.go @@ -41,10 +41,11 @@ func TestValidateKeyPackages(t *testing.T) { ctx := context.Background() firstResponse := svc.ValidateKeyPackagesResponse_ValidationResponse{ - IsOk: true, - WalletAddress: "0x123", - InstallationId: "123", - ErrorMessage: "", + IsOk: true, + WalletAddress: "0x123", + InstallationId: []byte("123"), + CredentialIdentityBytes: []byte("456"), + ErrorMessage: "", } mockGrpc.On("ValidateKeyPackages", ctx, mock.Anything).Return(&svc.ValidateKeyPackagesResponse{ @@ -55,7 +56,8 @@ func TestValidateKeyPackages(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1, len(res)) assert.Equal(t, "0x123", res[0].WalletAddress) - assert.Equal(t, "123", res[0].InstallationId) + assert.Equal(t, []byte("123"), res[0].InstallationId) + assert.Equal(t, []byte("456"), res[0].CredentialIdentity) } func TestValidateKeyPackagesError(t *testing.T) { diff --git a/pkg/store/query_test.go b/pkg/store/query_test.go index 0a57e96e..d3c2c028 100644 --- a/pkg/store/query_test.go +++ b/pkg/store/query_test.go @@ -294,7 +294,7 @@ func TestMlsMessagePublish(t *testing.T) { contentTopic := "foo" ctx := context.Background() - env, err := store.InsertMlsMessage(ctx, contentTopic, message) + env, err := store.InsertMLSMessage(ctx, contentTopic, message) require.NoError(t, err) require.Equal(t, env.ContentTopic, contentTopic) diff --git a/pkg/store/store.go b/pkg/store/store.go index b697b7d1..4a309044 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -144,7 +144,7 @@ func (s *Store) InsertMessage(env *messagev1.Envelope) (bool, error) { return stored, err } -func (s *Store) InsertMlsMessage(ctx context.Context, contentTopic string, data []byte) (*messagev1.Envelope, error) { +func (s *Store) InsertMLSMessage(ctx context.Context, contentTopic string, data []byte) (*messagev1.Envelope, error) { tmpEnvelope := &messagev1.Envelope{ ContentTopic: contentTopic, Message: data, diff --git a/pkg/topic/topic.go b/pkg/topic/topic.go index 473f141b..f0192bbf 100644 --- a/pkg/topic/topic.go +++ b/pkg/topic/topic.go @@ -43,6 +43,6 @@ func BuildGroupTopic(groupId string) string { return fmt.Sprintf("/xmtp/3/g-%s/proto", groupId) } -func BuildWelcomeTopic(installationId string) string { - return fmt.Sprintf("/xmtp/3/w-%s/proto", installationId) +func BuildWelcomeTopic(installationId []byte) string { + return fmt.Sprintf("/xmtp/3/w-%x/proto", installationId) } From 70d7c7dc947972fd0c79f5fffefe13cd0e8c4c61 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:09:29 -0700 Subject: [PATCH 28/33] Add missing credential identity --- pkg/api/message/v3/service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 3f79bdec..bb3945fb 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -55,8 +55,9 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI installationId := results[0].InstallationId walletAddress := results[0].WalletAddress + credentialIdentity := results[0].CredentialIdentity - if err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized); err != nil { + if err = s.mlsStore.CreateInstallation(ctx, installationId, walletAddress, req.LastResortKeyPackage.KeyPackageTlsSerialized, credentialIdentity); err != nil { return nil, err } From 168b78a1111d135b175453b2e7a27cd25c8beef7 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:27:35 -0700 Subject: [PATCH 29/33] Hack sql in query --- pkg/mlsstore/store.go | 102 ++++++++++++++++++++++++++----------- pkg/mlsstore/store_test.go | 2 +- 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 420bc4b3..307d3805 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -5,8 +5,9 @@ import ( "crypto/sha256" "database/sql" "encoding/hex" - "errors" + "fmt" "sort" + "strings" "time" "github.com/uptrace/bun" @@ -85,43 +86,86 @@ func (s *Store) InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage } func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) { - keyPackages := make([]*KeyPackage, 0) - err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { - err := tx.NewRaw(` - SELECT DISTINCT ON(installation_id) * FROM key_packages - WHERE "installation_id" IN (?) - AND not_consumed = TRUE - ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC - `, - bun.In(installationIds)). - Scan(ctx, &keyPackages) - - if err != nil { - return err - } - - if len(keyPackages) < len(installationIds) { - return errors.New("key packages not found") - } + tx, err := s.db.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { + return nil, err + } + defer tx.Rollback() - _, err = tx.NewUpdate(). - Table("key_packages"). - Set("consumed_at = ?", nowNs()). - Set("not_consumed = FALSE"). - Where("is_last_resort = FALSE"). - Where("id IN (?)", bun.In(extractIds(keyPackages))). - Exec(ctx) + sql, args := buildSelectKeyPackagesQuery(installationIds) + rows, err := tx.QueryContext(ctx, sql, args...) + if err != nil { + panic(err) + return nil, err + } + var keyPackages []*KeyPackage + if keyPackages, err = rowsToKeyPackages(rows); err != nil { + panic(err) + return nil, err + } - return err - }) + sql, args = buildConsumeKeyPackagesQuery(extractIds(keyPackages)) + fmt.Println(sql, args) + if _, err = tx.ExecContext(ctx, sql, args...); err != nil { + panic(err) + return nil, err + } - if err != nil { + if err = tx.Commit(); err != nil { return nil, err } return keyPackages, nil } +func buildSelectKeyPackagesQuery(installationIDs []InstallationId) (string, []interface{}) { + placeholders := make([]string, len(installationIDs)) + args := make([]interface{}, len(installationIDs)) + for i, id := range installationIDs { + placeholders[i] = fmt.Sprintf("$%d", i+1) + args[i] = id + } + + return ` + SELECT DISTINCT ON(installation_id) + id, installation_id, created_at, consumed_at, not_consumed, is_last_resort, data + FROM key_packages + WHERE "installation_id" IN (` + strings.Join(placeholders, ",") + `) + AND not_consumed = TRUE + ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC`, args +} + +func rowsToKeyPackages(rows *sql.Rows) ([]*KeyPackage, error) { + defer rows.Close() + var result []*KeyPackage + + for rows.Next() { + kp := &KeyPackage{} + var installationId InstallationId + if err := rows.Scan(&kp.ID, &installationId, &kp.CreatedAt, &kp.ConsumedAt, &kp.NotConsumed, &kp.IsLastResort, &kp.Data); err != nil { + return nil, err + } + kp.InstallationId = installationId + result = append(result, kp) + } + + return result, nil +} + +func buildConsumeKeyPackagesQuery(keyPackageIds []string) (string, []interface{}) { + args := []interface{}{nowNs()} + placeHolders := []string{} + for i, id := range keyPackageIds { + args = append(args, id) + placeHolders = append(placeHolders, fmt.Sprintf("$%d", i+2)) + } + + return `UPDATE key_packages + SET consumed_at = $1, not_consumed = FALSE + WHERE is_last_resort = FALSE + AND id in (` + strings.Join(placeHolders, ",") + `)`, args +} + func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) { updated := make([]*Installation, 0) // Find all installations that were changed since the startTimeNs diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index c610659f..bcb6ad78 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -130,7 +130,7 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := test.RandomBytes(32) + installationId := InstallationId(test.RandomBytes(32)) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) From 730065de04ef412274757b486d6a6b6e439aea05 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:27:41 -0700 Subject: [PATCH 30/33] Revert "Hack sql in query" This reverts commit 168b78a1111d135b175453b2e7a27cd25c8beef7. --- pkg/mlsstore/store.go | 102 +++++++++++-------------------------- pkg/mlsstore/store_test.go | 2 +- 2 files changed, 30 insertions(+), 74 deletions(-) diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 307d3805..420bc4b3 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -5,9 +5,8 @@ import ( "crypto/sha256" "database/sql" "encoding/hex" - "fmt" + "errors" "sort" - "strings" "time" "github.com/uptrace/bun" @@ -86,84 +85,41 @@ func (s *Store) InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage } func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) { - tx, err := s.db.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - sql, args := buildSelectKeyPackagesQuery(installationIds) - rows, err := tx.QueryContext(ctx, sql, args...) - if err != nil { - panic(err) - return nil, err - } - var keyPackages []*KeyPackage - if keyPackages, err = rowsToKeyPackages(rows); err != nil { - panic(err) - return nil, err - } - - sql, args = buildConsumeKeyPackagesQuery(extractIds(keyPackages)) - fmt.Println(sql, args) - if _, err = tx.ExecContext(ctx, sql, args...); err != nil { - panic(err) - return nil, err - } - - if err = tx.Commit(); err != nil { - return nil, err - } - - return keyPackages, nil -} - -func buildSelectKeyPackagesQuery(installationIDs []InstallationId) (string, []interface{}) { - placeholders := make([]string, len(installationIDs)) - args := make([]interface{}, len(installationIDs)) - for i, id := range installationIDs { - placeholders[i] = fmt.Sprintf("$%d", i+1) - args[i] = id - } - - return ` - SELECT DISTINCT ON(installation_id) - id, installation_id, created_at, consumed_at, not_consumed, is_last_resort, data - FROM key_packages - WHERE "installation_id" IN (` + strings.Join(placeholders, ",") + `) - AND not_consumed = TRUE - ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC`, args -} + keyPackages := make([]*KeyPackage, 0) + err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { + err := tx.NewRaw(` + SELECT DISTINCT ON(installation_id) * FROM key_packages + WHERE "installation_id" IN (?) + AND not_consumed = TRUE + ORDER BY installation_id ASC, is_last_resort ASC, created_at ASC + `, + bun.In(installationIds)). + Scan(ctx, &keyPackages) -func rowsToKeyPackages(rows *sql.Rows) ([]*KeyPackage, error) { - defer rows.Close() - var result []*KeyPackage + if err != nil { + return err + } - for rows.Next() { - kp := &KeyPackage{} - var installationId InstallationId - if err := rows.Scan(&kp.ID, &installationId, &kp.CreatedAt, &kp.ConsumedAt, &kp.NotConsumed, &kp.IsLastResort, &kp.Data); err != nil { - return nil, err + if len(keyPackages) < len(installationIds) { + return errors.New("key packages not found") } - kp.InstallationId = installationId - result = append(result, kp) - } - return result, nil -} + _, err = tx.NewUpdate(). + Table("key_packages"). + Set("consumed_at = ?", nowNs()). + Set("not_consumed = FALSE"). + Where("is_last_resort = FALSE"). + Where("id IN (?)", bun.In(extractIds(keyPackages))). + Exec(ctx) + + return err + }) -func buildConsumeKeyPackagesQuery(keyPackageIds []string) (string, []interface{}) { - args := []interface{}{nowNs()} - placeHolders := []string{} - for i, id := range keyPackageIds { - args = append(args, id) - placeHolders = append(placeHolders, fmt.Sprintf("$%d", i+2)) + if err != nil { + return nil, err } - return `UPDATE key_packages - SET consumed_at = $1, not_consumed = FALSE - WHERE is_last_resort = FALSE - AND id in (` + strings.Join(placeHolders, ",") + `)`, args + return keyPackages, nil } func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) { diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index bcb6ad78..c610659f 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -130,7 +130,7 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { defer cleanup() ctx := context.Background() - installationId := InstallationId(test.RandomBytes(32)) + installationId := test.RandomBytes(32) walletAddress := test.RandomString(32) keyPackage := test.RandomBytes(32) From e7a4f18459004b996142e140737dbe4f2112c302 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 25 Oct 2023 22:30:54 -0700 Subject: [PATCH 31/33] Remove custom type --- pkg/api/message/v3/service.go | 6 ++--- pkg/api/message/v3/service_test.go | 2 +- pkg/mlsstore/models.go | 38 ++++++++++-------------------- pkg/mlsstore/store.go | 12 +++++----- pkg/mlsstore/store_test.go | 6 ++--- 5 files changed, 25 insertions(+), 39 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index bb3945fb..8e2e7ffc 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -67,20 +67,20 @@ func (s *Service) RegisterInstallation(ctx context.Context, req *proto.RegisterI } func (s *Service) ConsumeKeyPackages(ctx context.Context, req *proto.ConsumeKeyPackagesRequest) (*proto.ConsumeKeyPackagesResponse, error) { - ids := mlsstore.InstallationIdArray(req.InstallationIds) + ids := req.InstallationIds keyPackages, err := s.mlsStore.ConsumeKeyPackages(ctx, ids) if err != nil { return nil, status.Errorf(codes.Internal, "failed to consume key packages: %s", err) } keyPackageMap := make(map[string]int) for idx, id := range ids { - keyPackageMap[id.String()] = idx + keyPackageMap[string(id)] = idx } resPackages := make([]*proto.ConsumeKeyPackagesResponse_KeyPackage, len(keyPackages)) for _, keyPackage := range keyPackages { - idx, ok := keyPackageMap[keyPackage.InstallationId.String()] + idx, ok := keyPackageMap[string(keyPackage.InstallationId)] if !ok { return nil, status.Errorf(codes.Internal, "could not find key package for installation") } diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index c353f617..cc8bf02e 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -43,7 +43,7 @@ func newMockedValidationService() *mockedMLSValidationService { return new(mockedMLSValidationService) } -func (m *mockedMLSValidationService) mockValidateKeyPackages(installationId mlsstore.InstallationId, walletAddress string) *mock.Call { +func (m *mockedMLSValidationService) mockValidateKeyPackages(installationId []byte, walletAddress string) *mock.Call { return m.On("ValidateKeyPackages", mock.Anything, mock.Anything).Return([]mlsvalidate.IdentityValidationResult{ { InstallationId: installationId, diff --git a/pkg/mlsstore/models.go b/pkg/mlsstore/models.go index 216d0e8f..226db843 100644 --- a/pkg/mlsstore/models.go +++ b/pkg/mlsstore/models.go @@ -2,38 +2,24 @@ package mlsstore import "github.com/uptrace/bun" -type InstallationId []byte - -func (id InstallationId) String() string { - return string(id) -} - type Installation struct { bun.BaseModel `bun:"table:installations"` - ID InstallationId `bun:",pk,type:bytea"` - WalletAddress string `bun:"wallet_address,notnull"` - CreatedAt int64 `bun:"created_at,notnull"` - RevokedAt *int64 `bun:"revoked_at"` - CredentialIdentity []byte `bun:"credential_identity,notnull,type:bytea"` + ID []byte `bun:",pk,type:bytea"` + WalletAddress string `bun:"wallet_address,notnull"` + CreatedAt int64 `bun:"created_at,notnull"` + RevokedAt *int64 `bun:"revoked_at"` + CredentialIdentity []byte `bun:"credential_identity,notnull,type:bytea"` } type KeyPackage struct { bun.BaseModel `bun:"table:key_packages"` - ID string `bun:",pk"` // ID is the hash of the data field - InstallationId InstallationId `bun:"installation_id,notnull,type:bytea"` - CreatedAt int64 `bun:"created_at,notnull"` - ConsumedAt *int64 `bun:"consumed_at"` - NotConsumed bool `bun:"not_consumed,default:true"` - IsLastResort bool `bun:"is_last_resort,notnull"` - Data []byte `bun:"data,notnull,type:bytea"` -} - -func InstallationIdArray(data [][]byte) []InstallationId { - result := make([]InstallationId, len(data)) - for i, d := range data { - result[i] = InstallationId(d) - } - return result + ID string `bun:",pk"` // ID is the hash of the data field + InstallationId []byte `bun:"installation_id,notnull,type:bytea"` + CreatedAt int64 `bun:"created_at,notnull"` + ConsumedAt *int64 `bun:"consumed_at"` + NotConsumed bool `bun:"not_consumed,default:true"` + IsLastResort bool `bun:"is_last_resort,notnull"` + Data []byte `bun:"data,notnull,type:bytea"` } diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index 420bc4b3..b0f83523 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -22,9 +22,9 @@ type Store struct { } type MlsStore interface { - CreateInstallation(ctx context.Context, installationId InstallationId, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error + CreateInstallation(ctx context.Context, installationId []byte, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage) error - ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) + ConsumeKeyPackages(ctx context.Context, installationIds [][]byte) ([]*KeyPackage, error) GetIdentityUpdates(ctx context.Context, walletAddresses []string, startTimeNs int64) (map[string]IdentityUpdateList, error) } @@ -43,7 +43,7 @@ func New(ctx context.Context, config Config) (*Store, error) { } // Creates the installation and last resort key package -func (s *Store) CreateInstallation(ctx context.Context, installationId InstallationId, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error { +func (s *Store) CreateInstallation(ctx context.Context, installationId []byte, walletAddress string, lastResortKeyPackage []byte, credentialIdentity []byte) error { createdAt := nowNs() installation := Installation{ @@ -84,7 +84,7 @@ func (s *Store) InsertKeyPackages(ctx context.Context, keyPackages []*KeyPackage return err } -func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds []InstallationId) ([]*KeyPackage, error) { +func (s *Store) ConsumeKeyPackages(ctx context.Context, installationIds [][]byte) ([]*KeyPackage, error) { keyPackages := make([]*KeyPackage, 0) err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error { err := tx.NewRaw(` @@ -164,7 +164,7 @@ func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string return out, nil } -func (s *Store) RevokeInstallation(ctx context.Context, installationId InstallationId) error { +func (s *Store) RevokeInstallation(ctx context.Context, installationId []byte) error { _, err := s.db.NewUpdate(). Model(&Installation{}). Set("revoked_at = ?", nowNs()). @@ -175,7 +175,7 @@ func (s *Store) RevokeInstallation(ctx context.Context, installationId Installat return err } -func NewKeyPackage(installationId InstallationId, data []byte, isLastResort bool) *KeyPackage { +func NewKeyPackage(installationId []byte, data []byte, isLastResort bool) *KeyPackage { return &KeyPackage{ ID: buildKeyPackageId(data), InstallationId: installationId, diff --git a/pkg/mlsstore/store_test.go b/pkg/mlsstore/store_test.go index c610659f..50673cec 100644 --- a/pkg/mlsstore/store_test.go +++ b/pkg/mlsstore/store_test.go @@ -118,7 +118,7 @@ func TestConsumeLastResortKeyPackage(t *testing.T) { err := store.CreateInstallation(ctx, installationId, walletAddress, keyPackage, keyPackage) require.NoError(t, err) - consumeResult, err := store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) + consumeResult, err := store.ConsumeKeyPackages(ctx, [][]byte{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) require.Equal(t, keyPackage, consumeResult[0].Data) @@ -146,13 +146,13 @@ func TestConsumeMultipleKeyPackages(t *testing.T) { Data: keyPackage2, }})) - consumeResult, err := store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) + consumeResult, err := store.ConsumeKeyPackages(ctx, [][]byte{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) require.Equal(t, keyPackage2, consumeResult[0].Data) require.Equal(t, installationId, consumeResult[0].InstallationId) - consumeResult, err = store.ConsumeKeyPackages(ctx, []InstallationId{installationId}) + consumeResult, err = store.ConsumeKeyPackages(ctx, [][]byte{installationId}) require.NoError(t, err) require.Len(t, consumeResult, 1) // Now we are out of regular key packages. Expect to consume the last resort From cdde011d918b3a084c5698227fd115a3739e8523 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 26 Oct 2023 08:50:06 -0700 Subject: [PATCH 32/33] Update to latest protos --- go.mod | 2 +- go.sum | 2 ++ pkg/api/message/v3/service.go | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1481fb76..e40e63bf 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/uptrace/bun/driver/pgdriver v1.1.16 github.com/waku-org/go-waku v0.8.0 github.com/xmtp/go-msgio v0.2.1-0.20220510223757-25a701b79cd3 - github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab + github.com/xmtp/proto/v3 v3.32.1-0.20231026053711-5efc208e3135 github.com/yoheimuta/protolint v0.39.0 go.uber.org/zap v1.24.0 golang.org/x/sync v0.3.0 diff --git a/go.sum b/go.sum index 9e9fac51..7ce0b101 100644 --- a/go.sum +++ b/go.sum @@ -1162,6 +1162,8 @@ github.com/xmtp/proto/v3 v3.29.1-0.20231025193535-9760f07c3401 h1:ctStMkU5570kEg github.com/xmtp/proto/v3 v3.29.1-0.20231025193535-9760f07c3401/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab h1:hWBftgxB7QWXDOOv1Wah6VZ6mwSFZX8e8rEGJQHm8zA= github.com/xmtp/proto/v3 v3.29.1-0.20231025220423-87413e63f3ab/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= +github.com/xmtp/proto/v3 v3.32.1-0.20231026053711-5efc208e3135 h1:MpeptLshF0T8ikwmVQI1mnlp5fXkbyUc+9FpyzFdsbo= +github.com/xmtp/proto/v3 v3.32.1-0.20231026053711-5efc208e3135/go.mod h1:NF2zAjtNpVIhS4tFG19g4L1tJcPZHm81oeDFXltmOiY= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/yoheimuta/go-protoparser/v4 v4.6.0 h1:uvz1e9/5Ihsm4Ku8AJeDImTpirKmIxubZdSn0QJNdnw= github.com/yoheimuta/go-protoparser/v4 v4.6.0/go.mod h1:AHNNnSWnb0UoL4QgHPiOAg2BniQceFscPI5X/BZNHl8= diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 8e2e7ffc..9bd34e9d 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -159,7 +159,7 @@ func (s *Service) PublishWelcomes(ctx context.Context, req *proto.PublishWelcome // TODO: Wrap this in a transaction so publishing is all or nothing for _, welcome := range req.WelcomeMessages { contentTopic := topic.BuildWelcomeTopic(welcome.InstallationId) - if err = s.publishMessage(ctx, contentTopic, welcome.WelcomeMessage.GetV1().Ciphertext); err != nil { + if err = s.publishMessage(ctx, contentTopic, welcome.WelcomeMessage.GetV1().WelcomeMessageTlsSerialized); err != nil { return nil, status.Errorf(codes.Internal, "failed to publish welcome message: %s", err) } } @@ -266,7 +266,7 @@ func validatePublishWelcomesRequest(req *proto.PublishWelcomesRequest) error { } v1 := welcome.WelcomeMessage.GetV1() - if v1 == nil || len(v1.Ciphertext) == 0 { + if v1 == nil || len(v1.WelcomeMessageTlsSerialized) == 0 { return status.Errorf(codes.InvalidArgument, "invalid welcome message") } } From 2a798ec10c4c194efb9d597448e216f5b2e7b394 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Thu, 26 Oct 2023 12:23:03 -0700 Subject: [PATCH 33/33] Add CredentialIdentity --- pkg/api/message/v3/service.go | 3 ++- pkg/api/message/v3/service_test.go | 2 ++ pkg/mlsstore/store.go | 7 ++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/api/message/v3/service.go b/pkg/api/message/v3/service.go index 9bd34e9d..e3d4140d 100644 --- a/pkg/api/message/v3/service.go +++ b/pkg/api/message/v3/service.go @@ -235,7 +235,8 @@ func buildIdentityUpdate(update mlsstore.IdentityUpdate) *proto.GetIdentityUpdat case mlsstore.Create: base.Kind = &proto.GetIdentityUpdatesResponse_Update_NewInstallation{ NewInstallation: &proto.GetIdentityUpdatesResponse_NewInstallationUpdate{ - InstallationId: update.InstallationId, + InstallationId: update.InstallationId, + CredentialIdentity: update.CredentialIdentity, }, } case mlsstore.Revoke: diff --git a/pkg/api/message/v3/service_test.go b/pkg/api/message/v3/service_test.go index cc8bf02e..a2d61679 100644 --- a/pkg/api/message/v3/service_test.go +++ b/pkg/api/message/v3/service_test.go @@ -285,6 +285,8 @@ func TestGetIdentityUpdates(t *testing.T) { require.NoError(t, err) require.NotNil(t, identityUpdates) require.Len(t, identityUpdates.Updates, 1) + require.Equal(t, identityUpdates.Updates[0].Updates[0].GetNewInstallation().InstallationId, installationId) + require.Equal(t, identityUpdates.Updates[0].Updates[0].GetNewInstallation().CredentialIdentity, []byte("test")) for _, walletUpdate := range identityUpdates.Updates { for _, update := range walletUpdate.Updates { diff --git a/pkg/mlsstore/store.go b/pkg/mlsstore/store.go index b0f83523..df2f392a 100644 --- a/pkg/mlsstore/store.go +++ b/pkg/mlsstore/store.go @@ -143,9 +143,10 @@ func (s *Store) GetIdentityUpdates(ctx context.Context, walletAddresses []string for _, installation := range updated { if installation.CreatedAt > startTimeNs { out[installation.WalletAddress] = append(out[installation.WalletAddress], IdentityUpdate{ - Kind: Create, - InstallationId: installation.ID, - TimestampNs: uint64(installation.CreatedAt), + Kind: Create, + InstallationId: installation.ID, + CredentialIdentity: installation.CredentialIdentity, + TimestampNs: uint64(installation.CreatedAt), }) } if installation.RevokedAt != nil && *installation.RevokedAt > startTimeNs {