mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-18 12:44:19 +01:00
Add AdminService (#176)
* add admin service * go mod tidy * fix linter: grpc.Dial * fix ocean get balance * fix linter * add .vscode to gitignore * rework admin balance API * fix mockedwallet in covenantless pkg * make proto
This commit is contained in:
@@ -7,11 +7,21 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port uint32
|
||||
NoTLS bool
|
||||
Port uint32
|
||||
NoTLS bool
|
||||
AuthUser string
|
||||
AuthPass string
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
if len(c.AuthUser) == 0 {
|
||||
return fmt.Errorf("missing auth user")
|
||||
}
|
||||
|
||||
if len(c.AuthPass) == 0 {
|
||||
return fmt.Errorf("missing auth password")
|
||||
}
|
||||
|
||||
lis, err := net.Listen("tcp", c.address())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port: %s", err)
|
||||
|
||||
121
server/internal/interface/grpc/handlers/adminservice.go
Normal file
121
server/internal/interface/grpc/handlers/adminservice.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/internal/core/application"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type adminHandler struct {
|
||||
adminService application.AdminService
|
||||
}
|
||||
|
||||
func NewAdminHandler(adminService application.AdminService) arkv1.AdminServiceServer {
|
||||
return &adminHandler{adminService}
|
||||
}
|
||||
|
||||
func (a *adminHandler) GetBalance(ctx context.Context, _ *arkv1.GetBalanceRequest) (*arkv1.GetBalanceResponse, error) {
|
||||
balance, err := a.adminService.GetBalance(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetBalanceResponse{
|
||||
MainAccount: &arkv1.Balance{
|
||||
Locked: convertSatoshis(balance.MainAccountBalance.Locked),
|
||||
Available: convertSatoshis(balance.MainAccountBalance.Available),
|
||||
},
|
||||
ConnectorsAccount: &arkv1.Balance{
|
||||
Locked: convertSatoshis(balance.ConnectorsAccountBalance.Locked),
|
||||
Available: convertSatoshis(balance.ConnectorsAccountBalance.Available),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) GetRoundDetails(ctx context.Context, req *arkv1.GetRoundDetailsRequest) (*arkv1.GetRoundDetailsResponse, error) {
|
||||
id := req.GetRoundId()
|
||||
if len(id) == 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "missing round id")
|
||||
}
|
||||
|
||||
details, err := a.adminService.GetRoundDetails(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetRoundDetailsResponse{
|
||||
RoundId: details.RoundId,
|
||||
Txid: details.TxId,
|
||||
ForfeitedAmount: convertSatoshis(details.ForfeitedAmount),
|
||||
TotalVtxosAmount: convertSatoshis(details.TotalVtxosAmount),
|
||||
TotalExitAmount: convertSatoshis(details.TotalExitAmount),
|
||||
FeesAmount: convertSatoshis(details.FeesAmount),
|
||||
InputsVtxos: details.InputsVtxos,
|
||||
OutputsVtxos: details.OutputsVtxos,
|
||||
ExitAddresses: details.ExitAddresses,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRounds implements arkv1.AdminServiceServer.
|
||||
func (a *adminHandler) GetRounds(ctx context.Context, req *arkv1.GetRoundsRequest) (*arkv1.GetRoundsResponse, error) {
|
||||
startAfter := req.GetAfter()
|
||||
startBefore := req.GetBefore()
|
||||
|
||||
if startAfter < 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid after (must be >= 0)")
|
||||
}
|
||||
|
||||
if startBefore < 0 {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid before (must be >= 0)")
|
||||
}
|
||||
|
||||
if startAfter >= startBefore {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid range")
|
||||
}
|
||||
|
||||
rounds, err := a.adminService.GetRounds(ctx, startAfter, startBefore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &arkv1.GetRoundsResponse{Rounds: rounds}, nil
|
||||
}
|
||||
|
||||
func (a *adminHandler) GetScheduledSweep(ctx context.Context, _ *arkv1.GetScheduledSweepRequest) (*arkv1.GetScheduledSweepResponse, error) {
|
||||
scheduledSweeps, err := a.adminService.GetScheduledSweeps(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sweeps := make([]*arkv1.ScheduledSweep, 0)
|
||||
|
||||
for _, sweep := range scheduledSweeps {
|
||||
outputs := make([]*arkv1.SweepableOutput, 0)
|
||||
|
||||
for _, output := range sweep.SweepableOutputs {
|
||||
outputs = append(outputs, &arkv1.SweepableOutput{
|
||||
Txid: output.TxId,
|
||||
Vout: output.Vout,
|
||||
ScheduledAt: output.ScheduledAt,
|
||||
Amount: convertSatoshis(output.Amount),
|
||||
})
|
||||
}
|
||||
|
||||
sweeps = append(sweeps, &arkv1.ScheduledSweep{
|
||||
RoundId: sweep.RoundId,
|
||||
Outputs: outputs,
|
||||
})
|
||||
}
|
||||
|
||||
return &arkv1.GetScheduledSweepResponse{Sweeps: sweeps}, nil
|
||||
}
|
||||
|
||||
// convert sats to string BTC
|
||||
func convertSatoshis(sats uint64) string {
|
||||
btc := float64(sats) * 1e-8
|
||||
return fmt.Sprintf("%.8f", btc)
|
||||
}
|
||||
42
server/internal/interface/grpc/interceptors/auth.go
Normal file
42
server/internal/interface/grpc/interceptors/auth.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package interceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func unaryAuthenticator(user, pass string) grpc.UnaryServerInterceptor {
|
||||
adminToken := fmt.Sprintf("%s:%s", user, pass)
|
||||
adminTokenEncoded := base64.StdEncoding.EncodeToString([]byte(adminToken))
|
||||
|
||||
return func(
|
||||
ctx context.Context,
|
||||
req interface{},
|
||||
info *grpc.UnaryServerInfo,
|
||||
handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
// whitelist the ArkService
|
||||
if strings.Contains(info.FullMethod, arkv1.ArkService_ServiceDesc.ServiceName) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
token, err := grpc_auth.AuthFromMD(ctx, "basic")
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "no basic header found: %v", err)
|
||||
}
|
||||
|
||||
if token != adminTokenEncoded {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "invalid auth credentials: %v", err)
|
||||
}
|
||||
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
)
|
||||
|
||||
// UnaryInterceptor returns the unary interceptor
|
||||
func UnaryInterceptor() grpc.ServerOption {
|
||||
return grpc.UnaryInterceptor(middleware.ChainUnaryServer(unaryLogger))
|
||||
func UnaryInterceptor(user, pass string) grpc.ServerOption {
|
||||
return grpc.UnaryInterceptor(middleware.ChainUnaryServer(
|
||||
unaryAuthenticator(user, pass),
|
||||
unaryLogger,
|
||||
))
|
||||
}
|
||||
|
||||
// StreamInterceptor returns the stream interceptor with a logrus log
|
||||
|
||||
@@ -40,7 +40,8 @@ func NewService(
|
||||
}
|
||||
|
||||
grpcConfig := []grpc.ServerOption{
|
||||
interceptors.UnaryInterceptor(), interceptors.StreamInterceptor(),
|
||||
interceptors.UnaryInterceptor(svcConfig.AuthUser, svcConfig.AuthPass),
|
||||
interceptors.StreamInterceptor(),
|
||||
}
|
||||
if !svcConfig.NoTLS {
|
||||
return nil, fmt.Errorf("tls termination not supported yet")
|
||||
@@ -53,8 +54,13 @@ func NewService(
|
||||
|
||||
// Server grpc.
|
||||
grpcServer := grpc.NewServer(grpcConfig...)
|
||||
|
||||
appHandler := handlers.NewHandler(appConfig.AppService())
|
||||
arkv1.RegisterArkServiceServer(grpcServer, appHandler)
|
||||
|
||||
adminHandler := handlers.NewAdminHandler(appConfig.AdminService())
|
||||
arkv1.RegisterAdminServiceServer(grpcServer, adminHandler)
|
||||
|
||||
healthHandler := handlers.NewHealthHandler()
|
||||
grpchealth.RegisterHealthServer(grpcServer, healthHandler)
|
||||
|
||||
@@ -91,6 +97,11 @@ func NewService(
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := arkv1.RegisterAdminServiceHandler(
|
||||
ctx, gwmux, conn,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grpcGateway := http.Handler(gwmux)
|
||||
|
||||
handler := router(grpcServer, grpcGateway)
|
||||
|
||||
Reference in New Issue
Block a user