Skip to content

Commit

Permalink
GO-3194 payments/nameservice protocol refactoring (#1084)
Browse files Browse the repository at this point in the history
* GO-3194 WiP

* GO-3194 Add MembershipGetVerificationEmailStatus

* GO-3194 Updated .proto file

* GO-3194 More comments

* GO-3194 Added NS name type

* GO-3194 Refactor nameservice, implement proto changes

* GO-3194 Linter
  • Loading branch information
AnthonyAkentiev authored Apr 8, 2024
1 parent 3a78649 commit f573352
Show file tree
Hide file tree
Showing 15 changed files with 4,360 additions and 2,835 deletions.
625 changes: 332 additions & 293 deletions clientlibrary/service/service.pb.go

Large diffs are not rendered by default.

120 changes: 8 additions & 112 deletions core/nameservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,135 +3,31 @@ package core
import (
"context"

"github.com/anyproto/any-sync/nameservice/nameserviceclient"
proto "github.com/anyproto/any-sync/nameservice/nameserviceproto"

"github.com/anyproto/anytype-heart/core/wallet"
"github.com/anyproto/anytype-heart/core/nameservice"
"github.com/anyproto/anytype-heart/pb"
)

// NameServiceResolveName does a name lookup: somename.any -> info
func (mw *Middleware) NameServiceResolveName(ctx context.Context, req *pb.RpcNameServiceResolveNameRequest) *pb.RpcNameServiceResolveNameResponse {
ns := getService[nameserviceclient.AnyNsClientService](mw)

var in proto.NameAvailableRequest
in.FullName = req.FullName

nar, err := ns.IsNameAvailable(ctx, &in)
if err != nil {
return &pb.RpcNameServiceResolveNameResponse{
Error: &pb.RpcNameServiceResolveNameResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceResolveNameResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceResolveNameResponse
out.Available = nar.Available
out.OwnerAnyAddress = nar.OwnerAnyAddress
// EOA is onwer of -> SCW is owner of -> name
out.OwnerEthAddress = nar.OwnerEthAddress
out.OwnerScwEthAddress = nar.OwnerScwEthAddress
out.SpaceId = nar.SpaceId
out.NameExpires = nar.NameExpires

return &out
ns := getService[nameservice.Service](mw)
return ns.NameServiceResolveName(ctx, req)
}

func (mw *Middleware) NameServiceResolveAnyId(ctx context.Context, req *pb.RpcNameServiceResolveAnyIdRequest) *pb.RpcNameServiceResolveAnyIdResponse {
// Get name service object that connects to the remote "namingNode"
// in order for that to work, we need to have a "namingNode" node in the nodes section of the config
ns := getService[nameserviceclient.AnyNsClientService](mw)

var in proto.NameByAnyIdRequest
in.AnyAddress = req.AnyId

nar, err := ns.GetNameByAnyId(ctx, &in)
if err != nil {
return &pb.RpcNameServiceResolveAnyIdResponse{
Error: &pb.RpcNameServiceResolveAnyIdResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceResolveAnyIdResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceResolveAnyIdResponse
out.Found = nar.Found
out.FullName = nar.Name

return &out
ns := getService[nameservice.Service](mw)
return ns.NameServiceResolveAnyId(ctx, req)
}

func (mw *Middleware) NameServiceResolveSpaceId(ctx context.Context, req *pb.RpcNameServiceResolveSpaceIdRequest) *pb.RpcNameServiceResolveSpaceIdResponse {
// TODO: implement
// TODO: test

return &pb.RpcNameServiceResolveSpaceIdResponse{
Error: &pb.RpcNameServiceResolveSpaceIdResponseError{
Code: pb.RpcNameServiceResolveSpaceIdResponseError_UNKNOWN_ERROR,
Description: "not implemented",
Description: "TODO - not implemented yet",
},
}
}

func (mw *Middleware) NameServiceUserAccountGet(ctx context.Context, req *pb.RpcNameServiceUserAccountGetRequest) *pb.RpcNameServiceUserAccountGetResponse {
// 1 - get name service object that connects to the remote "namingNode"
// in order for that to work, we need to have a "namingNode" node in the nodes section of the config
ns := getService[nameserviceclient.AnyNsClientService](mw)

// 2 - get user's ETH address from the wallet
w := getService[wallet.Wallet](mw)

// 3 - get user's account info
//
// when AccountAbstraction is used to deploy a smart contract wallet
// then name is really owned by this SCW, but owner of this SCW is
// EOA that was used to sign transaction
//
// EOA (w.GetAccountEthAddress()) -> SCW (ua.OwnerSmartContracWalletAddress) -> name
var guar proto.GetUserAccountRequest
guar.OwnerEthAddress = w.GetAccountEthAddress().Hex()

ua, err := ns.GetUserAccount(ctx, &guar)
if err != nil {
return &pb.RpcNameServiceUserAccountGetResponse{
Error: &pb.RpcNameServiceUserAccountGetResponseError{
Code: pb.RpcNameServiceUserAccountGetResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// 4 - check if any name is attached to the account (reverse resolve the name)
var in proto.NameByAddressRequest

// NOTE: we are passing here SCW address, not initial ETH address!
// read comment about SCW above please
in.OwnerScwEthAddress = ua.OwnerSmartContracWalletAddress

nar, err := ns.GetNameByAddress(ctx, &in)
if err != nil {
return &pb.RpcNameServiceUserAccountGetResponse{
Error: &pb.RpcNameServiceUserAccountGetResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceUserAccountGetResponseError_BAD_NAME_RESOLVE,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceUserAccountGetResponse
out.NamesCountLeft = ua.NamesCountLeft
out.OperationsCountLeft = ua.OperationsCountLeft
// not checking nar.Found here, no need
out.AnyNameAttached = nar.Name

return &out
ns := getService[nameservice.Service](mw)
return ns.NameServiceUserAccountGet(ctx, req)
}
152 changes: 152 additions & 0 deletions core/nameservice/nameservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package nameservice

import (
"context"

"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/nameservice/nameserviceclient"

"github.com/anyproto/anytype-heart/core/wallet"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"

proto "github.com/anyproto/any-sync/nameservice/nameserviceproto"
)

const CName = "nameservice"

func NsNameToFullName(nsName string, nsNameType model.NameserviceNameType) string {
if nsNameType == model.NameserviceNameType_AnyName {
return nsName + ".any"
}

// by default return it
return nsName
}

type Service interface {
NameServiceResolveName(ctx context.Context, req *pb.RpcNameServiceResolveNameRequest) *pb.RpcNameServiceResolveNameResponse
NameServiceResolveAnyId(ctx context.Context, req *pb.RpcNameServiceResolveAnyIdRequest) *pb.RpcNameServiceResolveAnyIdResponse
NameServiceUserAccountGet(ctx context.Context, req *pb.RpcNameServiceUserAccountGetRequest) *pb.RpcNameServiceUserAccountGetResponse

app.Component
}

func New() Service {
return &service{}
}

type service struct {
nsclient nameserviceclient.AnyNsClientService
wallet wallet.Wallet
}

func (s *service) Name() (name string) {
return CName
}

func (s *service) Init(a *app.App) (err error) {
// Get name service object that connects to the remote "namingNode"
// in order for that to work, we need to have a "namingNode" node in the nodes section of the config
s.nsclient = app.MustComponent[nameserviceclient.AnyNsClientService](a)
s.wallet = app.MustComponent[wallet.Wallet](a)
return nil
}

func (s *service) NameServiceResolveName(ctx context.Context, req *pb.RpcNameServiceResolveNameRequest) *pb.RpcNameServiceResolveNameResponse {
var in proto.NameAvailableRequest
in.FullName = NsNameToFullName(req.NsName, req.NsNameType)

nar, err := s.nsclient.IsNameAvailable(ctx, &in)
if err != nil {
return &pb.RpcNameServiceResolveNameResponse{
Error: &pb.RpcNameServiceResolveNameResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceResolveNameResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceResolveNameResponse
out.Available = nar.Available
out.OwnerAnyAddress = nar.OwnerAnyAddress
// EOA is onwer of -> SCW is owner of -> name
out.OwnerEthAddress = nar.OwnerEthAddress
out.OwnerScwEthAddress = nar.OwnerScwEthAddress
out.SpaceId = nar.SpaceId
out.NameExpires = nar.NameExpires

return &out
}

func (s *service) NameServiceResolveAnyId(ctx context.Context, req *pb.RpcNameServiceResolveAnyIdRequest) *pb.RpcNameServiceResolveAnyIdResponse {
var in proto.NameByAnyIdRequest
in.AnyAddress = req.AnyId

nar, err := s.nsclient.GetNameByAnyId(ctx, &in)
if err != nil {
return &pb.RpcNameServiceResolveAnyIdResponse{
Error: &pb.RpcNameServiceResolveAnyIdResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceResolveAnyIdResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceResolveAnyIdResponse
out.Found = nar.Found
out.FullName = nar.Name

return &out
}

func (s *service) NameServiceUserAccountGet(ctx context.Context, req *pb.RpcNameServiceUserAccountGetRequest) *pb.RpcNameServiceUserAccountGetResponse {
// when AccountAbstraction is used to deploy a smart contract wallet
// then name is really owned by this SCW, but owner of this SCW is
// EOA that was used to sign transaction
//
// EOA (w.GetAccountEthAddress()) -> SCW (ua.OwnerSmartContracWalletAddress) -> name
var guar proto.GetUserAccountRequest
guar.OwnerEthAddress = s.wallet.GetAccountEthAddress().Hex()

ua, err := s.nsclient.GetUserAccount(ctx, &guar)
if err != nil {
return &pb.RpcNameServiceUserAccountGetResponse{
Error: &pb.RpcNameServiceUserAccountGetResponseError{
Code: pb.RpcNameServiceUserAccountGetResponseError_UNKNOWN_ERROR,
Description: err.Error(),
},
}
}

// 4 - check if any name is attached to the account (reverse resolve the name)
var in proto.NameByAddressRequest

// NOTE: we are passing here SCW address, not initial ETH address!
// read comment about SCW above please
in.OwnerScwEthAddress = ua.OwnerSmartContracWalletAddress

nar, err := s.nsclient.GetNameByAddress(ctx, &in)
if err != nil {
return &pb.RpcNameServiceUserAccountGetResponse{
Error: &pb.RpcNameServiceUserAccountGetResponseError{
// we don't map error codes here
Code: pb.RpcNameServiceUserAccountGetResponseError_BAD_NAME_RESOLVE,
Description: err.Error(),
},
}
}

// Return the response
var out pb.RpcNameServiceUserAccountGetResponse
out.NamesCountLeft = ua.NamesCountLeft
out.OperationsCountLeft = ua.OperationsCountLeft
// not checking nar.Found here, no need
out.AnyNameAttached = nar.Name

return &out
}
20 changes: 20 additions & 0 deletions core/nameservice/nameservice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package nameservice

import (
"testing"

"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/stretchr/testify/require"
)

func TestNsNameToFullName(t *testing.T) {
t.Run("should succeed", func(t *testing.T) {
out := NsNameToFullName("somename", model.NameserviceNameType_AnyName)
require.Equal(t, "somename.any", out)

// by default return no suffix without error
// in this case other NS methods should check the validity and return an error
out = NsNameToFullName("tony", 1)
require.Equal(t, "tony", out)
})
}
10 changes: 10 additions & 0 deletions core/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ func (mw *Middleware) MembershipGetVerificationEmail(ctx context.Context, req *p
return out
}

func (mw *Middleware) MembershipGetVerificationEmailStatus(ctx context.Context, req *pb.RpcMembershipGetVerificationEmailStatusRequest) *pb.RpcMembershipGetVerificationEmailStatusResponse {
// TODO:
return &pb.RpcMembershipGetVerificationEmailStatusResponse{
Error: &pb.RpcMembershipGetVerificationEmailStatusResponseError{
Code: pb.RpcMembershipGetVerificationEmailStatusResponseError_UNKNOWN_ERROR,
Description: "TODO - not implemented yet",
},
}
}

func (mw *Middleware) MembershipVerifyEmailCode(ctx context.Context, req *pb.RpcMembershipVerifyEmailCodeRequest) *pb.RpcMembershipVerifyEmailCodeResponse {
ps := getService[payments.Service](mw)
out, err := ps.VerifyEmailCode(ctx, req)
Expand Down
Loading

0 comments on commit f573352

Please sign in to comment.