From b590daf305d03bf7add2ee88feee8d48f506bc26 Mon Sep 17 00:00:00 2001 From: optclblast Date: Sun, 26 May 2024 13:42:06 +0300 Subject: [PATCH] deploy multisig implemented but tests needed --- backend/README.md | 18 +++++-- backend/internal/factory/interface.go | 2 + backend/internal/factory/wire_gen.go | 2 +- .../interface/rest/controllers/chain.go | 36 +++++++++---- backend/internal/interface/rest/server.go | 2 +- backend/internal/pkg/models/models.go | 1 + backend/internal/pkg/models/user.go | 4 ++ .../usecase/interactors/chain/chain.go | 54 ++++++++++++++++++- .../interactors/organizations/interactor.go | 2 + .../repository/organizations/repository.go | 8 +++ .../repository/transactions/repository.go | 2 +- .../usecase/repository/users/repository.go | 11 ++++ 12 files changed, 123 insertions(+), 19 deletions(-) diff --git a/backend/README.md b/backend/README.md index 25707aa..72e3f65 100644 --- a/backend/README.md +++ b/backend/README.md @@ -508,16 +508,28 @@ Response: ### Request body: * title (string) * owners (array of object { "public_key":"string" }) -* confirmations (int) +* confirmations (uint) ### Example Request: ``` bash - +curl --request POST \ + --url http://localhost:8081/organizations/018fb246-1616-7f1b-9fe2-1a3202224695/multisig \ + --header 'Authorization: Bearer token' \ + --header 'content-type: application/json' \ + --data '{ + "title":"new sig", + "owners":[ + "0x5810f45ac87c0be03b4d8174132e2bc81ba1a928" + ], + "confirmations":1 +}' ``` Response: ``` json - +{ + "Ok": true +} ``` diff --git a/backend/internal/factory/interface.go b/backend/internal/factory/interface.go index 0937835..14fe5d8 100644 --- a/backend/internal/factory/interface.go +++ b/backend/internal/factory/interface.go @@ -95,12 +95,14 @@ func provideTxController( log *slog.Logger, txInteractor transactions.TransactionsInteractor, chainInteractor chain.ChainInteractor, + organizationsInteractor organizations.OrganizationsInteractor, ) controllers.TransactionsController { return controllers.NewTransactionsController( log.WithGroup("transactions-controller"), txInteractor, presenters.NewTransactionsPresenter(), chainInteractor, + organizationsInteractor, ) } diff --git a/backend/internal/factory/wire_gen.go b/backend/internal/factory/wire_gen.go index e66f3d1..ca403a5 100644 --- a/backend/internal/factory/wire_gen.go +++ b/backend/internal/factory/wire_gen.go @@ -35,7 +35,7 @@ func ProvideService(c config.Config) (service.Service, func(), error) { organizationsPresenter := provideOrganizationsPresenter() organizationsController := provideOrganizationsController(logger, organizationsInteractor, organizationsPresenter) transactionsInteractor := provideTxInteractor(logger, transactionsRepository, organizationsInteractor) - transactionsController := provideTxController(logger, transactionsInteractor, chainInteractor) + transactionsController := provideTxController(logger, transactionsInteractor, chainInteractor, organizationsInteractor) participantsController := provideParticipantsController(logger, organizationsInteractor, usersInteractor) rootController := provideControllers(logger, authController, organizationsController, transactionsController, participantsController) server := provideRestServer(logger, rootController, c, jwtInteractor) diff --git a/backend/internal/interface/rest/controllers/chain.go b/backend/internal/interface/rest/controllers/chain.go index dc390a2..3443c96 100644 --- a/backend/internal/interface/rest/controllers/chain.go +++ b/backend/internal/interface/rest/controllers/chain.go @@ -15,6 +15,7 @@ import ( "github.com/emochka2007/block-accounting/internal/pkg/ctxmeta" "github.com/emochka2007/block-accounting/internal/pkg/models" "github.com/emochka2007/block-accounting/internal/usecase/interactors/chain" + "github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations" "github.com/emochka2007/block-accounting/internal/usecase/interactors/transactions" "github.com/ethereum/go-ethereum/common" ) @@ -36,10 +37,11 @@ type TransactionsController interface { } type transactionsController struct { - log *slog.Logger - txInteractor transactions.TransactionsInteractor - txPresenter presenters.TransactionsPresenter - chainInteractor chain.ChainInteractor + log *slog.Logger + txInteractor transactions.TransactionsInteractor + txPresenter presenters.TransactionsPresenter + chainInteractor chain.ChainInteractor + organizationsInteractor organizations.OrganizationsInteractor } func NewTransactionsController( @@ -47,12 +49,14 @@ func NewTransactionsController( txInteractor transactions.TransactionsInteractor, txPresenter presenters.TransactionsPresenter, chainInteractor chain.ChainInteractor, + organizationsInteractor organizations.OrganizationsInteractor, ) TransactionsController { return &transactionsController{ - log: log, - txInteractor: txInteractor, - txPresenter: txPresenter, - chainInteractor: chainInteractor, + log: log, + txInteractor: txInteractor, + txPresenter: txPresenter, + chainInteractor: chainInteractor, + organizationsInteractor: organizationsInteractor, } } @@ -221,18 +225,28 @@ func (c *transactionsController) NewMultisig(w http.ResponseWriter, r *http.Requ ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) defer cancel() - ownersPKs := make([]string, len(req.Owners)) + ownersPKs := make([][]byte, len(req.Owners)) for i, pk := range req.Owners { - ownersPKs[i] = pk.PublicKey + ownersPKs[i] = common.Hex2Bytes(pk.PublicKey[2:]) } if req.Confirmations <= 0 { req.Confirmations = 1 } + participants, err := c.organizationsInteractor.Participants(ctx, organizations.ParticipantsParams{ + PKs: ownersPKs, + OrganizationID: organizationID, + UsersOnly: true, + ActiveOnly: true, + }) + if err != nil { + return nil, fmt.Errorf("error fetch participants by pks. %w", err) + } + if err := c.chainInteractor.NewMultisig(ctx, chain.NewMultisigParams{ - OwnersPKs: ownersPKs, + Owners: participants, Confirmations: req.Confirmations, }); err != nil { return nil, fmt.Errorf("error deploy multisig. %w", err) diff --git a/backend/internal/interface/rest/server.go b/backend/internal/interface/rest/server.go index 6f45a83..6156ccc 100644 --- a/backend/internal/interface/rest/server.go +++ b/backend/internal/interface/rest/server.go @@ -144,7 +144,7 @@ func (s *Server) buildRouter() { r.Post("/invite", s.handle(s.controllers.Auth.Invite, "invite")) r.Route("/{participant_id}", func(r chi.Router) { - r.Get("/", nil) + r.Get("/", nil) // todo если успею }) }) }) diff --git a/backend/internal/pkg/models/models.go b/backend/internal/pkg/models/models.go index 51d9db9..fc88ca2 100644 --- a/backend/internal/pkg/models/models.go +++ b/backend/internal/pkg/models/models.go @@ -9,6 +9,7 @@ import ( type Multisig struct { ID uuid.UUID Title string + Address []byte OrganizationID uuid.UUID Owners []OrganizationParticipant ConfirmationsRequired int diff --git a/backend/internal/pkg/models/user.go b/backend/internal/pkg/models/user.go index ff6c45e..fc779d6 100644 --- a/backend/internal/pkg/models/user.go +++ b/backend/internal/pkg/models/user.go @@ -55,6 +55,10 @@ func (u *User) Seed() []byte { return u.Bip39Seed } +func (u *User) PublicKey() []byte { + return u.PK +} + type OrganizationParticipantType int const ( diff --git a/backend/internal/usecase/interactors/chain/chain.go b/backend/internal/usecase/interactors/chain/chain.go index d3c5506..071e5ba 100644 --- a/backend/internal/usecase/interactors/chain/chain.go +++ b/backend/internal/usecase/interactors/chain/chain.go @@ -8,12 +8,14 @@ import ( "io" "log/slog" "net/http" + "time" "github.com/emochka2007/block-accounting/internal/pkg/config" "github.com/emochka2007/block-accounting/internal/pkg/ctxmeta" "github.com/emochka2007/block-accounting/internal/pkg/models" "github.com/emochka2007/block-accounting/internal/usecase/repository/transactions" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" ) type ChainInteractor interface { @@ -41,10 +43,15 @@ func NewChainInteractor( } type NewMultisigParams struct { - OwnersPKs []string + Title string + Owners []models.OrganizationParticipant Confirmations int } +type newMultisigChainResponse struct { + Address string `json:"address"` +} + func (i *chainInteractor) NewMultisig(ctx context.Context, params NewMultisigParams) error { endpoint := i.config.ChainAPI.Host + "/multi-sig/deploy" @@ -54,8 +61,18 @@ func (i *chainInteractor) NewMultisig(ctx context.Context, params NewMultisigPar slog.Any("params", params), ) + pks := make([]string, len(params.Owners)) + + for i, owner := range params.Owners { + if owner.GetUser() == nil { + return fmt.Errorf("error invalis owners set") + } + + pks[i] = "0x" + common.Bytes2Hex(owner.GetUser().PublicKey()) + } + requestBody, err := json.Marshal(map[string]any{ - "owners": params.OwnersPKs, + "owners": pks, "confirmations": params.Confirmations, }) if err != nil { @@ -67,6 +84,11 @@ func (i *chainInteractor) NewMultisig(ctx context.Context, params NewMultisigPar return fmt.Errorf("error fetch user from context. %w", err) } + organizationID, err := ctxmeta.OrganizationId(ctx) + if err != nil { + return fmt.Errorf("error fetch organization id from context. %w", err) + } + body := bytes.NewBuffer(requestBody) req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body) @@ -90,6 +112,34 @@ func (i *chainInteractor) NewMultisig(ctx context.Context, params NewMultisigPar defer resp.Body.Close() + raw, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("error read body. %w", err) + } + + respObject := new(newMultisigChainResponse) + + if err := json.Unmarshal(raw, &respObject); err != nil { + return fmt.Errorf("error parse chain-api response body. %w", err) + } + + multisigAddress := common.Hex2Bytes(respObject.Address) + + createdAt := time.Now() + + if err := i.txRepository.AddMultisig(ctx, models.Multisig{ + ID: uuid.Must(uuid.NewV7()), + Title: params.Title, + Address: multisigAddress, + OrganizationID: organizationID, + Owners: params.Owners, + ConfirmationsRequired: params.Confirmations, + CreatedAt: createdAt, + UpdatedAt: createdAt, + }); err != nil { + return fmt.Errorf("error add new multisig. %w", err) + } + i.log.Debug( "deploy multisig response", slog.Int("code", resp.StatusCode), diff --git a/backend/internal/usecase/interactors/organizations/interactor.go b/backend/internal/usecase/interactors/organizations/interactor.go index 947d123..f88c856 100644 --- a/backend/internal/usecase/interactors/organizations/interactor.go +++ b/backend/internal/usecase/interactors/organizations/interactor.go @@ -49,6 +49,7 @@ type ParticipantParams struct { type ParticipantsParams struct { IDs uuid.UUIDs OrganizationID uuid.UUID + PKs [][]byte UsersOnly bool ActiveOnly bool @@ -289,6 +290,7 @@ func (i *organizationsInteractor) Participants( participants, err := i.orgRepository.Participants(ctx, organizations.ParticipantsParams{ Ids: params.IDs, OrganizationId: params.OrganizationID, + PKs: params.PKs, UsersOnly: params.UsersOnly, EmployeesOnly: params.EmployeesOnly, ActiveOnly: params.ActiveOnly, diff --git a/backend/internal/usecase/repository/organizations/repository.go b/backend/internal/usecase/repository/organizations/repository.go index 020e17d..d60e7c5 100644 --- a/backend/internal/usecase/repository/organizations/repository.go +++ b/backend/internal/usecase/repository/organizations/repository.go @@ -31,6 +31,7 @@ type GetParams struct { type ParticipantsParams struct { OrganizationId uuid.UUID Ids uuid.UUIDs + PKs [][]byte // Filters UsersOnly bool @@ -388,6 +389,12 @@ func (r *repositorySQL) Participants( ) } + if len(params.PKs) > 0 { + ouQuery = ouQuery.InnerJoin("users as u on u.id = ou.user_id").Where(sq.Eq{ + "u.public_key": params.PKs, + }) + } + rows, err := ouQuery.RunWith(r.Conn(ctx)).QueryContext(ctx) if err != nil { return fmt.Errorf("error fetch organization participants. %w", err) @@ -548,6 +555,7 @@ func (r *repositorySQL) Participants( usrs, err = r.usersRepository.Get(usersCtx, users.GetParams{ Ids: ids, + PKs: params.PKs, }) if err != nil { return fmt.Errorf("error fetch users by ids. %w", err) diff --git a/backend/internal/usecase/repository/transactions/repository.go b/backend/internal/usecase/repository/transactions/repository.go index 8129fcd..15443ff 100644 --- a/backend/internal/usecase/repository/transactions/repository.go +++ b/backend/internal/usecase/repository/transactions/repository.go @@ -435,7 +435,7 @@ func (r *repositorySQL) AddMultisig( owner.Id(), multisig.CreatedAt, multisig.UpdatedAt, - ) + ).PlaceholderFormat(sq.Dollar) if _, err := addOwnerQuery.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil { return fmt.Errorf("error insert multisig owner data. %w", err) diff --git a/backend/internal/usecase/repository/users/repository.go b/backend/internal/usecase/repository/users/repository.go index 5f73373..904e425 100644 --- a/backend/internal/usecase/repository/users/repository.go +++ b/backend/internal/usecase/repository/users/repository.go @@ -15,6 +15,7 @@ import ( type GetParams struct { Ids uuid.UUIDs + PKs [][]byte OrganizationId uuid.UUID Seed []byte } @@ -60,6 +61,12 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us }) } + if len(params.PKs) > 0 { + query = query.Where(sq.Eq{ + "u.public_key": params.PKs, + }) + } + if params.OrganizationId != uuid.Nil { query = query.InnerJoin( "organizations_users as ou on ou.user_id = u.id", @@ -72,6 +79,10 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us query = query.Where("u.seed = ?", params.Seed) } + if params.PKs != nil { + fmt.Println(query.ToSql()) + } + rows, err := query.RunWith(r.Conn(ctx)).QueryContext(ctx) if err != nil { return fmt.Errorf("error fetch data from database. %w", err)