deploy multisig implemented but tests needed

This commit is contained in:
r8zavetr8v 2024-05-26 13:42:06 +03:00
parent 05d802630e
commit b590daf305
12 changed files with 123 additions and 19 deletions

View File

@ -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
}
```

View File

@ -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,
)
}

View File

@ -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)

View File

@ -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"
)
@ -40,6 +41,7 @@ type transactionsController struct {
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,
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)

View File

@ -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 если успею
})
})
})

View File

@ -9,6 +9,7 @@ import (
type Multisig struct {
ID uuid.UUID
Title string
Address []byte
OrganizationID uuid.UUID
Owners []OrganizationParticipant
ConfirmationsRequired int

View File

@ -55,6 +55,10 @@ func (u *User) Seed() []byte {
return u.Bip39Seed
}
func (u *User) PublicKey() []byte {
return u.PK
}
type OrganizationParticipantType int
const (

View File

@ -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),

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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)