package grpcadapter import ( "context" "crypto/subtle" domain "gl/domain/ledger" basev1 "gl/gen/base/v1" ledgerv1 "gl/gen/ledger/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) func (h *Handler) requireAdmin(ctx context.Context) error { values := metadata.ValueFromIncomingContext(ctx, "x-admin-token") if h.adminToken == "" || len(values) != 1 || subtle.ConstantTimeCompare([]byte(values[0]), []byte(h.adminToken)) != 1 { return status.Error(codes.PermissionDenied, "invalid administrative token") } return nil } func modeMessage(state domain.OperatingState) *ledgerv1.OperatingModeResponse { return &ledgerv1.OperatingModeResponse{Mode: string(state.Mode), Revision: state.Revision, ActorId: state.ActorID, Reason: state.Reason, ChangedAt: timestampMessage(state.ChangedAt)} } func (h *Handler) GetOperatingMode(ctx context.Context, _ *basev1.Empty) (*ledgerv1.OperatingModeResponse, error) { state, err := h.ledger.OperatingMode(ctx) if err != nil { return nil, rpcError(err) } return modeMessage(state), nil } func (h *Handler) SetOperatingMode(ctx context.Context, request *ledgerv1.SetOperatingModeRequest) (*ledgerv1.OperatingModeResponse, error) { if err := h.requireAdmin(ctx); err != nil { return nil, err } state, err := h.ledger.SetOperatingMode(ctx, domain.OperatingMode(request.GetMode()), request.GetExpectedRevision(), request.GetActorId(), request.GetReason(), request.GetReconciliationConfirmed()) if err != nil { return nil, rpcError(err) } return modeMessage(state), nil } func (h *Handler) PostInternalJournal(ctx context.Context, request *ledgerv1.AppendJournalRequest) (*ledgerv1.AppendJournalResponse, error) { if err := h.requireAdmin(ctx); err != nil { return nil, err } command, err := appendJournalCommand(request) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } result, err := h.ledger.PostInternalJournal(ctx, command) if err != nil { return nil, rpcError(err) } return &ledgerv1.AppendJournalResponse{Journal: journalMessage(result.Journal), AlreadyExisted: result.AlreadyExisted}, nil } func (h *Handler) GetUserBalances(ctx context.Context, request *ledgerv1.GetUserBalancesRequest) (*ledgerv1.GetUserBalancesResponse, error) { if request == nil { return nil, status.Error(codes.InvalidArgument, "request is required") } balances, err := h.ledger.UserBalances(ctx, request.GetOwnerType(), request.GetOwnerId(), request.AssetId) if err != nil { return nil, rpcError(err) } response := &ledgerv1.GetUserBalancesResponse{OwnerType: request.GetOwnerType(), OwnerId: request.GetOwnerId()} for _, balance := range balances { response.Balances = append(response.Balances, &ledgerv1.UserAssetBalance{ AssetId: balance.AssetID, Available: balance.Available.String(), Frozen: balance.Frozen.String(), Total: balance.Available.Add(balance.Frozen).String(), }) } return response, nil }