Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go.work.sum

# Program binary
/vpn-indexer
/vpn-cli

# Local data dir
/.vpn-indexer
31 changes: 25 additions & 6 deletions internal/api/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package api
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
Expand Down Expand Up @@ -79,8 +80,14 @@ func (a *Api) handleTxSignup(w http.ResponseWriter, r *http.Request) {
"error",
err,
)
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
var validationErr txbuilder.InputValidationError
if errors.As(err, &validationErr) {
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, `{"error":"Invalid request: %s"}`, validationErr)
} else {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
}
return
}

Expand Down Expand Up @@ -146,8 +153,14 @@ func (a *Api) handleTxRenew(w http.ResponseWriter, r *http.Request) {
"error",
err,
)
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
var validationErr txbuilder.InputValidationError
if errors.As(err, &validationErr) {
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, `{"error":"Invalid request: %s"}`, validationErr)
} else {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
}
return
}

Expand Down Expand Up @@ -210,8 +223,14 @@ func (a *Api) handleTxTransfer(w http.ResponseWriter, r *http.Request) {
"error",
err,
)
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
var validationErr txbuilder.InputValidationError
if errors.As(err, &validationErr) {
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, `{"error":"Invalid request: %s"}`, validationErr)
} else {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"error":"Internal server error"}`))
}
return
}

Expand Down
13 changes: 8 additions & 5 deletions internal/txbuilder/renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"time"

Expand All @@ -41,6 +40,10 @@ func BuildRenewTransferTx(
price int,
duration int,
) ([]byte, error) {
// Validate inputs
if paymentAddress == "" {
return nil, NewInputValidationError("empty payment address provided")
}
cfg := config.GetConfig()
cc, err := apolloBackend()
if err != nil {
Expand All @@ -58,15 +61,15 @@ func BuildRenewTransferTx(
// Decode payment address
paymentAddr, err := serAddress.DecodeAddress(paymentAddress)
if err != nil {
return nil, fmt.Errorf("payment address: %w", err)
return nil, NewInputValidationError("failed to decode payment address")
}
// Determine owner credential
// Use existing owner for client by default
ownerCredential := client.Credential
if ownerAddress != "" && ownerAddress != paymentAddress {
ownerAddr, err := serAddress.DecodeAddress(ownerAddress)
if err != nil {
return nil, fmt.Errorf("owner address: %w", err)
return nil, NewInputValidationError("failed to decode owner address")
}
ownerCredential = ownerAddr.PaymentPart
}
Expand Down Expand Up @@ -113,7 +116,7 @@ func BuildRenewTransferTx(
return nil, fmt.Errorf("choose input UTxOs: %w", err)
}
if len(inputUtxos) == 0 {
return nil, errors.New("no input UTxOs found")
return nil, NewInputValidationError("no input UTxOs found")
}
// Lookup UTxO for client asset
clientUtxo, err := cc.GetUtxoFromRef(
Expand All @@ -130,7 +133,7 @@ func BuildRenewTransferTx(
if price > 0 && duration > 0 {
selectionId, err = determinePlanSelection(refData, price, duration)
if err != nil {
return nil, fmt.Errorf("determine plan selection: %w", err)
return nil, NewInputValidationError("could not determine plan selection from provided price/duration")
}
}
// Get last known slot
Expand Down
25 changes: 21 additions & 4 deletions internal/txbuilder/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ func BuildSignupTx(
duration int,
region string,
) ([]byte, []byte, error) {
// Validate inputs
if region == "" {
return nil, nil, NewInputValidationError("empty region provided")
}
if paymentAddress == "" {
return nil, nil, NewInputValidationError("empty payment address provided")
}
cfg := config.GetConfig()
cc, err := apolloBackend()
if err != nil {
Expand All @@ -53,14 +60,14 @@ func BuildSignupTx(
// Decode payment address
paymentAddr, err := serAddress.DecodeAddress(paymentAddress)
if err != nil {
return nil, nil, fmt.Errorf("payment address: %w", err)
return nil, nil, NewInputValidationError("failed to decode payment address")
}
// Determine owner credential
ownerCredential := paymentAddr.PaymentPart
if ownerAddress != "" && ownerAddress != paymentAddress {
ownerAddr, err := serAddress.DecodeAddress(ownerAddress)
if err != nil {
return nil, nil, fmt.Errorf("owner address: %w", err)
return nil, nil, NewInputValidationError("failed to decode owner address")
}
ownerCredential = ownerAddr.PaymentPart
}
Expand All @@ -87,6 +94,16 @@ func BuildSignupTx(
default:
return nil, nil, errors.New("reference data not provided (missing deps.Ref and deps.DB)")
}
// Validate region
foundRegion := false
for _, refDataRegion := range refData.Regions {
if region == refDataRegion.Name {
foundRegion = true
}
}
if !foundRegion {
return nil, nil, NewInputValidationError("provided region not valid")
}
// Parse script ref
scriptRef, err := inputRefFromString(cfg.TxBuilder.ScriptRefInput)
if err != nil {
Expand All @@ -107,14 +124,14 @@ func BuildSignupTx(
return nil, nil, fmt.Errorf("choose input UTxOs: %w", err)
}
if len(inputUtxos) == 0 {
return nil, nil, errors.New("no input UTxOs found")
return nil, nil, NewInputValidationError("no input UTxOs found")
}
// Determine client ID from first selected input UTxO
clientId := clientIdFromInput(inputUtxos[0].Input)
// Determine plan selection ID from price/duration
selectionId, err := determinePlanSelection(refData, price, duration)
if err != nil {
return nil, nil, fmt.Errorf("determine plan selection: %w", err)
return nil, nil, NewInputValidationError("could not determine plan selection from provided price/duration")
}
// Get last known slot
curSlot, err := cc.LastBlockSlot()
Expand Down
15 changes: 15 additions & 0 deletions internal/txbuilder/txbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,18 @@ func determinePlanSelection(
}
return 0, errors.New("selection not found")
}

// InputValidationError is a custom error type representing input validation errors
type InputValidationError struct {
msg string
}

func NewInputValidationError(msg string) error {
return InputValidationError{
msg: msg,
}
}

func (e InputValidationError) Error() string {
return e.msg
}
Loading