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
61 changes: 37 additions & 24 deletions packages/core-go/address/errors.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,49 @@
package address

import "fmt"

// ErrorCode represents specific error types for pattern matching
type ErrorCode string

const (
ErrInvalidChecksum ErrorCode = "INVALID_CHECKSUM"
ErrInvalidLength ErrorCode = "INVALID_LENGTH"
ErrInvalidBase32 ErrorCode = "INVALID_BASE32"
ErrRejectedSeedKey ErrorCode = "REJECTED_SEED_KEY"
ErrRejectedPreauth ErrorCode = "REJECTED_PREAUTH"
ErrRejectedHashX ErrorCode = "REJECTED_HASH_X"
ErrFederationAddressNotSupported ErrorCode = "FEDERATION_ADDRESS_NOT_SUPPORTED"
ErrUnknownPrefix ErrorCode = "UNKNOWN_PREFIX"
ErrInvalidChecksum ErrorCode = "INVALID_CHECKSUM"
ErrInvalidLength ErrorCode = "INVALID_LENGTH"
ErrInvalidBase32 ErrorCode = "INVALID_BASE32"
ErrUnknownPrefix ErrorCode = "UNKNOWN_PREFIX"
ErrRejectedSeedKey ErrorCode = "REJECTED_SEED_KEY"
ErrRejectedPreauth ErrorCode = "REJECTED_PREAUTH"
ErrRejectedHashX ErrorCode = "REJECTED_HASH_X"
ErrFederationAddressNotSupported ErrorCode = "FEDERATION_ADDRESS_NOT_SUPPORTED"
)

// Error variables
var (
ErrInvalidChecksumError = fmt.Errorf(string(ErrInvalidChecksum))
ErrInvalidBase32Error = fmt.Errorf(string(ErrInvalidBase32))
ErrInvalidLengthError = fmt.Errorf(string(ErrInvalidLength))
ErrUnknownVersionByteError = fmt.Errorf("unknown version byte")
)
// RoutingError is the main custom error type
type RoutingError struct {
Code ErrorCode
Input string
Message string
}

// AddressError represents an address-related error
type AddressError struct {
Code ErrorCode
Input string
Message string
func (e RoutingError) Error() string {
return e.Message
}

func (e *AddressError) Error() string {
return e.Message
// Is allows errors.Is to match by Code
func (e RoutingError) Is(target error) bool {
if targetErr, ok := target.(RoutingError); ok {
return e.Code == targetErr.Code
}
return false
}

// Common error variables
var (
ErrInvalidChecksumError = RoutingError{Code: ErrInvalidChecksum, Message: "invalid checksum"}
ErrInvalidBase32Error = RoutingError{Code: ErrInvalidBase32, Message: "invalid base32 encoding"}
ErrInvalidLengthError = RoutingError{Code: ErrInvalidLength, Message: "invalid address length"}
ErrUnknownPrefixError = RoutingError{Code: ErrUnknownPrefix, Message: "unknown address prefix"}
ErrFederationAddressNotSupportedError = RoutingError{Code: ErrFederationAddressNotSupported, Message: "federation address not supported"}
)

// Legacy / additional error for version byte
var ErrUnknownVersionByteError = RoutingError{
Code: ErrUnknownPrefix,
Message: "unknown version byte",
}
103 changes: 47 additions & 56 deletions packages/core-go/address/parse.go
Original file line number Diff line number Diff line change
@@ -1,68 +1,59 @@
package address

import (
"encoding/binary"
"strings"
"encoding/binary"
"errors"
"strings"
)

// Parse parses a Stellar address string into an Address struct.
// For muxed accounts (KindM), BaseG and MuxedID are populated; for other kinds,
// these fields will be empty/zero. Returns an AddressError if validation fails.
func Parse(input string) (*Address, error) {
kind, err := Detect(input)
if err != nil {
code := ErrUnknownPrefix
switch err {
case ErrInvalidChecksumError:
code = ErrInvalidChecksum
case ErrInvalidBase32Error:
code = ErrInvalidBase32
case ErrInvalidLengthError:
code = ErrInvalidLength
case ErrUnknownVersionByteError:
code = ErrUnknownPrefix
}

return nil, &AddressError{
Code: code,
Input: input,
Message: err.Error(),
}
}

raw := strings.ToUpper(input)

addr := &Address{
Kind: kind,
Raw: raw,
}

// For muxed accounts, also populate BaseG and MuxedID.
if kind == KindM {
versionByte, payload, err := DecodeStrKey(raw)
if err != nil {
return nil, err
}
if versionByte != VersionByteM {
return nil, ErrUnknownVersionByteError
}

if len(payload) != 40 {
return nil, ErrInvalidLengthError
}

pubkey := payload[:32]
id := binary.BigEndian.Uint64(payload[32:40])
kind, err := Detect(input)
if err != nil {
var code ErrorCode = ErrUnknownPrefix

switch {
case errors.Is(err, ErrInvalidChecksumError):
code = ErrInvalidChecksum
case errors.Is(err, ErrInvalidBase32Error):
code = ErrInvalidBase32
case errors.Is(err, ErrInvalidLengthError):
code = ErrInvalidLength
}

baseG, err := EncodeStrKey(VersionByteG, pubkey)
if err != nil {
return nil, err
}
return nil, RoutingError{
Code: code,
Input: input,
Message: err.Error(),
}
}

addr.BaseG = baseG
addr.MuxedID = id
}
raw := strings.ToUpper(input)
addr := &Address{
Kind: kind,
Raw: raw,
}

return addr, nil
if kind == KindM {
versionByte, payload, err := DecodeStrKey(raw)
if err != nil {
return nil, err
}
if versionByte != VersionByteM {
return nil, ErrUnknownPrefixError
}
if len(payload) != 40 {
return nil, ErrInvalidLengthError
}
pubkey := payload[:32]
id := binary.BigEndian.Uint64(payload[32:40])
baseG, err := EncodeStrKey(VersionByteG, pubkey)
if err != nil {
return nil, err
}
addr.BaseG = baseG
addr.MuxedID = id
}

return addr, nil
}
15 changes: 3 additions & 12 deletions packages/core-go/routing/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,12 @@ func ExtractRouting(input RoutingInput) RoutingResult {

parsed, err := address.Parse(input.Destination)
if err != nil {
addrErr, ok := err.(*address.AddressError)
if !ok {
addrErr = &address.AddressError{
Code: address.ErrUnknownPrefix,
Input: input.Destination,
Message: err.Error(),
}
}

return RoutingResult{
RoutingSource: "none",
Warnings: []address.Warning{},
DestinationError: &DestinationError{
Code: addrErr.Code,
Message: addrErr.Message,
Code: address.ErrUnknownPrefix,
Message: err.Error(),
},
}
}
Expand Down Expand Up @@ -182,4 +173,4 @@ func ExtractRouting(input RoutingInput) RoutingResult {

func stringValue(s string) string {
return s
}
}
Loading