package sdk import ( "encoding/json" "fmt" "os" "strings" ) func LoadOrGenerateIdentity(path string) (*Identity, error) { data, err := os.ReadFile(path) if err == nil { return parseIdentity(strings.TrimSpace(string(data))) } if !os.IsNotExist(err) { return nil, err } id := GenerateIdentity() encoded, err := serializeIdentity(id) if err != nil { return nil, err } dir := dirname(path) if dir != "" { if e := os.MkdirAll(dir, 0755); e != nil { return nil, e } } if e := os.WriteFile(path, []byte(encoded), 0600); e != nil { return nil, e } return id, nil } type identityJSON struct { PublicKey string `json:"public_key"` PrivateKey string `json:"private_key"` Address string `json:"address"` } func serializeIdentity(id *Identity) (string, error) { data, err := json.Marshal(identityJSON{ PublicKey: id.PublicKeyHex(), PrivateKey: id.PrivateKeyHex(), Address: id.Address.String(), }) if err != nil { return "", err } return string(data), nil } func parseIdentity(data string) (*Identity, error) { var j identityJSON if err := json.Unmarshal([]byte(data), &j); err != nil { return nil, fmt.Errorf("parse identity: %w", err) } if j.PrivateKey == "" { return nil, fmt.Errorf("invalid identity: no private key") } pub, err := hexDecode(j.PublicKey) if err != nil { return nil, fmt.Errorf("decode public key: %w", err) } priv, err := hexDecode(j.PrivateKey) if err != nil { return nil, fmt.Errorf("decode private key: %w", err) } return &Identity{ PublicKey: pub, PrivateKey: priv, Address: AddressFromPublicKey(pub), }, nil } func hexDecode(s string) ([]byte, error) { out, err := hexDecodeSimple(s) if err != nil { return nil, err } return out, nil } func hexDecodeSimple(s string) ([]byte, error) { h := func(c byte) (byte, error) { switch { case '0' <= c && c <= '9': return c - '0', nil case 'a' <= c && c <= 'f': return c - 'a' + 10, nil case 'A' <= c && c <= 'F': return c - 'A' + 10, nil default: return 0, fmt.Errorf("invalid hex") } } if len(s)%2 != 0 { return nil, fmt.Errorf("odd hex length") } out := make([]byte, len(s)/2) for i := 0; i < len(s); i += 2 { hi, e1 := h(s[i]) lo, e2 := h(s[i+1]) if e1 != nil || e2 != nil { return nil, fmt.Errorf("invalid hex char") } out[i/2] = hi<<4 | lo } return out, nil } func dirname(path string) string { idx := strings.LastIndex(path, "/") if idx == -1 { idx = strings.LastIndex(path, "\\") } if idx == -1 { return "" } return path[:idx] }