diff --git a/packages/core-go/address/detect.go b/packages/core-go/address/detect.go index def03d2..ffd5b4e 100644 --- a/packages/core-go/address/detect.go +++ b/packages/core-go/address/detect.go @@ -6,14 +6,15 @@ func Detect(addr string) (AddressKind, error) { return "", err } - switch versionByte { - case VersionByteG: - return KindG, nil - case VersionByteM: - return KindM, nil - case VersionByteC: - return KindC, nil - default: - return "", ErrUnknownVersionByteError +func Detect(address string) string { + if strkey.IsValidEd25519PublicKey(address) { + return "G" } + if strkey.IsValidMuxedAccountEd25519PublicKey(address) { + return "M" + } + if _, err := strkey.Decode(strkey.VersionByteContract, address); err == nil { + return "C" + } + return "invalid" } diff --git a/packages/core-go/address/result.go b/packages/core-go/address/result.go index 0255f54..2c035a8 100644 --- a/packages/core-go/address/result.go +++ b/packages/core-go/address/result.go @@ -6,3 +6,9 @@ type ParseResult struct { Warnings []Warning Err *AddressError } + +type AddressError struct { + Code ErrorCode + Input string + Message string +} diff --git a/packages/core-go/muxed/decode.go b/packages/core-go/muxed/decode.go index 12e81b2..9008bf6 100644 --- a/packages/core-go/muxed/decode.go +++ b/packages/core-go/muxed/decode.go @@ -7,28 +7,16 @@ import ( "github.com/stellar-address-kit/core-go/address" ) -// muxedPayloadSize is the expected raw payload length after stripping the -// version byte and checksum: 8 bytes (uint64 ID) + 32 bytes (Ed25519 key). -const muxedPayloadSize = 40 - -func DecodeMuxed(mAddress string) (string, uint64, error) { - _, payload, err := address.DecodeStrKey(mAddress) +func DecodeMuxed(mAddress string) (string, string, error) { + muxedAccount, err := strkey.DecodeMuxedAccount(mAddress) if err != nil { - return "", 0, err + return "", "", err } - if len(payload) != muxedPayloadSize { - return "", 0, fmt.Errorf("invalid muxed address payload length: %d", len(payload)) - } - - // Muxed payload layout: [32-byte Ed25519 key][8-byte big-endian uint64 ID] - keyBytes := payload[:32] - id := binary.BigEndian.Uint64(payload[32:]) - - baseG, err := address.EncodeStrKey(address.VersionByteG, keyBytes) + baseG, err := muxedAccount.AccountID() if err != nil { return "", 0, err } - return baseG, id, nil + return baseG, strconv.FormatUint(muxedAccount.ID(), 10), nil } diff --git a/packages/core-go/muxed/encode.go b/packages/core-go/muxed/encode.go index 0a56b66..6db9224 100644 --- a/packages/core-go/muxed/encode.go +++ b/packages/core-go/muxed/encode.go @@ -2,15 +2,18 @@ package muxed import ( "fmt" - "strconv" + "math/big" "github.com/stellar/go/strkey" ) func EncodeMuxed(baseG string, id string) (string, error) { - idUint, err := strconv.ParseUint(id, 10, 64) - if err != nil { - return "", fmt.Errorf("invalid muxed account id %q: %w", id, err) + idInt := new(big.Int) + if _, ok := idInt.SetString(id, 10); !ok { + return "", fmt.Errorf("invalid muxed account id %q", id) + } + if idInt.Sign() < 0 || idInt.BitLen() > 64 { + return "", fmt.Errorf("muxed account id %q exceeds uint64", id) } var muxedAccount strkey.MuxedAccount @@ -18,6 +21,6 @@ func EncodeMuxed(baseG string, id string) (string, error) { return "", err } - muxedAccount.SetID(idUint) + muxedAccount.SetID(idInt.Uint64()) return muxedAccount.Address() } diff --git a/packages/core-go/routing/extract.go b/packages/core-go/routing/extract.go index 17b05aa..c1a1a4a 100644 --- a/packages/core-go/routing/extract.go +++ b/packages/core-go/routing/extract.go @@ -77,8 +77,8 @@ func ExtractRouting(input RoutingInput) RoutingResult { } } - if parsed.Kind == address.KindM { - baseG, id, err := muxed.DecodeMuxed(parsed.Raw) + if parsed.Kind == "M" { + baseG, id, err := muxed.DecodeMuxed(parsed.Address) if err != nil { return RoutingResult{ RoutingSource: "none", @@ -90,9 +90,10 @@ func ExtractRouting(input RoutingInput) RoutingResult { } } - warnings := []address.Warning{} + warnings := append([]address.Warning{}, parsed.Warnings...) + memoValue := stringValue(input.MemoValue) - if input.MemoType == "id" || (input.MemoType == "text" && digitsOnlyRegex.MatchString(input.MemoValue)) { + if input.MemoType == "id" || (input.MemoType == "text" && digitsOnlyRegex.MatchString(memoValue)) { warnings = append(warnings, address.Warning{ Code: address.WarnMemoPresentWithMuxed, Severity: "warn", @@ -108,19 +109,26 @@ func ExtractRouting(input RoutingInput) RoutingResult { return RoutingResult{ DestinationBaseAccount: baseG, - RoutingID: &id, + RoutingID: NewRoutingID(id), RoutingSource: "muxed", Warnings: warnings, } } - routingID := (*uint64)(nil) + var routingID *RoutingID routingSource := "none" - warnings := []address.Warning{} + warnings := append([]address.Warning{}, parsed.Warnings...) + memoValue := stringValue(input.MemoValue) if input.MemoType == "id" { - val, parseErr := strconv.ParseUint(input.MemoValue, 10, 64) - if parseErr != nil { + norm := NormalizeMemoTextID(memoValue) + if norm.Normalized != "" { + routingID = NewRoutingID(norm.Normalized) + routingSource = "memo" + } + warnings = append(warnings, norm.Warnings...) + + if norm.Normalized == "" { warnings = append(warnings, address.Warning{ Code: address.WarnMemoIDInvalidFormat, Severity: "warn", @@ -142,11 +150,10 @@ func ExtractRouting(input RoutingInput) RoutingResult { routingID = &val routingSource = "memo" } - } else if input.MemoType == "text" && input.MemoValue != "" { - norm := NormalizeMemoTextID(input.MemoValue) + } else if input.MemoType == "text" && memoValue != "" { + norm := NormalizeMemoTextID(memoValue) if norm.Normalized != "" { - parsedID, _ := strconv.ParseUint(norm.Normalized, 10, 64) - routingID = &parsedID + routingID = NewRoutingID(norm.Normalized) routingSource = "memo" warnings = append(warnings, norm.Warnings...) } else { @@ -182,4 +189,12 @@ func ExtractRouting(input RoutingInput) RoutingResult { RoutingSource: routingSource, Warnings: warnings, } -} \ No newline at end of file +} + +func stringValue(s *string) string { + if s == nil { + return "" + } + + return *s +} diff --git a/packages/core-go/routing/result.go b/packages/core-go/routing/result.go index 137eedd..d0374c4 100644 --- a/packages/core-go/routing/result.go +++ b/packages/core-go/routing/result.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "strconv" + + "github.com/stellar-address-kit/core-go/address" ) type MemoType string @@ -65,3 +67,16 @@ func (r *RoutingID) Uint64() (uint64, error) { func NewRoutingID(s string) *RoutingID { return &RoutingID{raw: s} } + +type RoutingResult struct { + DestinationBaseAccount string + RoutingID *RoutingID + RoutingSource string // "muxed" | "memo" | "none" + Warnings []address.Warning + DestinationError *DestinationError +} + +type DestinationError struct { + Code address.ErrorCode + Message string +}