mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
Feat/1285 es ucan formatting (#1302)
* feat: Add Enclave Usage Examples * feat(es/ucan): Add comprehensive integration tests - Create integration.test.ts with full UCAN token lifecycle testing - Cover end-to-end token creation, parsing, and validation - Test capability attenuation and delegation chains - Validate multi-algorithm support and timestamp scenarios - Implement error recovery and performance test scenarios 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * No commit suggestions generated * chore: Remove migrated components and add migration documentation Removed all code and references for components that have been moved to separate repositories: **Moved to sonr-io/hway:** - bridge/ - HTTP service with OAuth2/OIDC/WebAuthn handlers - cmd/hway/ - Highway service binary - internal/migrations/ - PostgreSQL schema migrations **Moved to sonr-io/motr:** - cmd/motr/ - Motor worker service (WASM vault operations) - cmd/vault/ - Vault CLI tool - crypto/ - Comprehensive cryptographic library - packages/ - TypeScript SDK packages (es, sdk, ui, com, pkl) - web/auth/ - Authentication web application - web/dash/ - Dashboard web application **Updated Configuration:** - Makefile: Removed build/test/release targets for moved components - CLAUDE.md: Simplified to focus on core blockchain components - devbox.json: Removed scripts for moved services - docker-compose.yml: Removed hway, postgres, redis, auth, dash services - .github/scopes.yml: Removed CI scopes for migrated components - .goreleaser.yml: Updated release configuration **Added Migration Documentation:** - MIGRATE_HWAY.md: Comprehensive Highway service architecture and migration guide - MIGRATE_MOTR.md: Comprehensive Motor/Worker/Vault architecture and migration guide These migration documents provide complete context for setting up the new repositories including architecture diagrams, component breakdowns, API documentation, and migration checklists. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * chore: Remove contracts references and documentation Removed all references to the contracts directory that was migrated to a separate repository. **Changes:** - .gitignore: Removed contract-specific ignore patterns for DAO and wSNR contracts - .gitignore: Removed hway and motr binary references (already migrated) - .rgignore: Removed contracts, chains, and crypto directory references - docs/reference/contracts/: Removed DAO.mdx and wSNR.mdx documentation files This completes the cleanup of migrated components from the repository. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: add crypto library migration documentation Added comprehensive migration documentation for the crypto library that was moved to sonr-io/crypto repository. This documentation provides complete context for understanding the cryptographic primitives and protocols used throughout the Sonr ecosystem. ## Key Documentation Added ### MIGRATE_CRYPTO.md Complete documentation of the crypto library covering: **Core Cryptographic Primitives** - Elliptic curve implementations (Ed25519, Secp256k1, P-256, BLS12-381, Pallas/Vesta) - Native curve arithmetic with optimized field operations - Pairing-friendly curves for BLS signatures **Multi-Party Computation (MPC)** - MPC enclave for vault key generation and management - Threshold cryptography (TECDSA, TED25519 with FROST protocol) - Distributed Key Generation (DKG) via Gennaro and FROST protocols - Secret sharing schemes (Shamir, Feldman VSS, Pedersen VSS) **Digital Signature Schemes** - BLS signatures with aggregation support - BBS+ signatures for selective disclosure - Schnorr signatures (standard and Mina/NEM variants) - ECDSA with deterministic nonce generation **Zero-Knowledge Proofs** - Bulletproofs for range proofs - Inner Product Arguments (IPA) - Batch verification support **Advanced Cryptographic Protocols** - Cryptographic accumulators for set membership proofs - Paillier homomorphic encryption - Oblivious Transfer (OT) protocols - Verifiable Random Functions (VRF) **Key Management & Identity** - DID key management with multi-chain support - Multi-algorithm public key handling - Wallet address derivation (Bitcoin, Ethereum, Cosmos, Solana, etc.) **UCAN Integration** - User-Controlled Authorization Networks - Capability delegation and attenuation - JWT-based capability tokens - MPC-enabled UCAN signing **Security Utilities** - AEAD encryption (AES-GCM, AES-SIV) - Argon2 key derivation - ECIES encryption - Secure memory handling ### MIGRATE_MOTR.md Updates Updated Motor migration documentation to clarify that the crypto library is now a separate external dependency at github.com/sonr-io/crypto v1.0.1 ## Repository Context The crypto library has been successfully migrated to its own repository and is published as a Go module. It serves as the foundational cryptographic layer for: - Sonr blockchain (snrd) - DID signatures, vault operations - Highway service (hway) - UCAN token signing, WebAuthn - Motor/Worker (motr) - MPC vault operations, threshold signatures ## Integration Impact All Sonr ecosystem components now depend on the external crypto library: ```go require github.com/sonr-io/crypto v1.0.1 ``` The migration enables independent versioning and maintenance of cryptographic primitives while maintaining security and compatibility across the ecosystem. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * No commit suggestions generated * No commit suggestions generated --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
[tool.commitizen]
|
||||
name = "cz_customize"
|
||||
tag_format = "hway/v$version"
|
||||
ignored_tag_formats = ["*/v${version}", "v${version}"]
|
||||
version_scheme = "semver"
|
||||
version_provider = "scm"
|
||||
update_changelog_on_bump = true
|
||||
changelog_file = "CHANGELOG.md"
|
||||
major_version_zero = true
|
||||
annotated_tag = true
|
||||
pre_bump_hooks = ["bash scripts/hook-bump-pre.sh"]
|
||||
post_bump_hooks = ["goreleaser release --clean -f cmd/hway/.goreleaser.yml"]
|
||||
|
||||
[tool.commitizen.customize]
|
||||
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
|
||||
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
|
||||
default_bump = "PATCH"
|
||||
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(hway\\)(!)?:"
|
||||
@@ -1,293 +0,0 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
|
||||
---
|
||||
version: 2
|
||||
dist: dist/hway
|
||||
monorepo:
|
||||
tag_prefix: hway/
|
||||
dir: cmd/hway
|
||||
|
||||
project_name: hway
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
|
||||
builds:
|
||||
# Hway Darwin AMD64
|
||||
- id: hway-darwin-amd64
|
||||
main: .
|
||||
binary: hway
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}}
|
||||
- -X main.Commit={{.Commit}}
|
||||
- -s -w # Strip debug info
|
||||
|
||||
# Hway Darwin ARM64
|
||||
- id: hway-darwin-arm64
|
||||
main: .
|
||||
binary: hway
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=oa64-clang
|
||||
- CXX=oa64-clang++
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- arm64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}}
|
||||
- -X main.Commit={{.Commit}}
|
||||
- -s -w # Strip debug info
|
||||
|
||||
# Hway Linux AMD64
|
||||
- id: hway-linux-amd64
|
||||
main: .
|
||||
binary: hway
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-linux-gnu-gcc
|
||||
- CXX=x86_64-linux-gnu-g++
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}}
|
||||
- -X main.Commit={{.Commit}}
|
||||
- -s -w
|
||||
|
||||
# Hway Linux ARM64
|
||||
- id: hway-linux-arm64
|
||||
main: .
|
||||
binary: hway
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
- CXX=aarch64-linux-gnu-g++
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}}
|
||||
- -X main.Commit={{.Commit}}
|
||||
- -s -w
|
||||
|
||||
aur_sources:
|
||||
- name: hway
|
||||
disable: true
|
||||
homepage: "https://sonr.io"
|
||||
description: "Highway service - bridge between the Public Internet and Sonr blockchain"
|
||||
maintainers:
|
||||
- "Sonr <support@sonr.io>"
|
||||
license: "GPL-3.0"
|
||||
private_key: "{{ .Env.AUR_KEY }}"
|
||||
git_url: "ssh://[email protected]/hway.git"
|
||||
skip_upload: auto
|
||||
provides:
|
||||
- hway
|
||||
conflicts:
|
||||
- hway-bin
|
||||
depends:
|
||||
- glibc
|
||||
- redis
|
||||
optdepends:
|
||||
- "docker: for container-based vault operations"
|
||||
makedepends:
|
||||
- go
|
||||
- git
|
||||
- make
|
||||
commit_msg_template: "Update to {{ .Tag }}"
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: "prad@sonr.io"
|
||||
prepare: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
go mod download
|
||||
build: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
export CGO_ENABLED=1
|
||||
export CGO_CPPFLAGS="${CPPFLAGS}"
|
||||
export CGO_CFLAGS="${CFLAGS}"
|
||||
export CGO_CXXFLAGS="${CXXFLAGS}"
|
||||
export CGO_LDFLAGS="${LDFLAGS}"
|
||||
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||
go build \
|
||||
-ldflags="-w -s -buildid='' -linkmode=external \
|
||||
-X main.Version=${pkgver}" \
|
||||
-o hway ./cmd/hway
|
||||
chmod +x ./hway
|
||||
package: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
|
||||
# bin
|
||||
install -Dm755 "./hway" "${pkgdir}/usr/bin/hway"
|
||||
|
||||
# license
|
||||
if [ -f "./LICENSE" ]; then
|
||||
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/hway/LICENSE"
|
||||
fi
|
||||
|
||||
# readme
|
||||
if [ -f "./README.md" ]; then
|
||||
install -Dm644 "./README.md" "${pkgdir}/usr/share/doc/hway/README.md"
|
||||
fi
|
||||
|
||||
nix:
|
||||
- name: hway
|
||||
ids:
|
||||
- hway
|
||||
homepage: "https://sonr.io"
|
||||
description: "Highway network component for Sonr"
|
||||
license: "gpl3"
|
||||
path: pkgs/hway/default.nix
|
||||
commit_msg_template: "hway: {{ .Tag }}"
|
||||
dependencies:
|
||||
- stdenv
|
||||
- glibc
|
||||
extra_install: |-
|
||||
wrapProgram $out/bin/hway --prefix PATH : ${lib.makeBinPath [ glibc stdenv.cc.cc.lib ]}
|
||||
repository:
|
||||
owner: sonr-io
|
||||
name: nur
|
||||
branch: main
|
||||
token: "{{ .Env.GITHUB_TOKEN }}"
|
||||
|
||||
archives:
|
||||
- id: hway
|
||||
ids:
|
||||
- hway-linux-amd64
|
||||
- hway-linux-arm64
|
||||
- hway-darwin-amd64
|
||||
- hway-darwin-arm64
|
||||
name_template: >-
|
||||
hway_{{ .Os }}_{{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }}
|
||||
formats: ["tar.gz"]
|
||||
files:
|
||||
- src: README*
|
||||
wrap_in_directory: false
|
||||
|
||||
homebrew_casks:
|
||||
- name: hway
|
||||
ids:
|
||||
- hway
|
||||
homepage: "https://sonr.io"
|
||||
description: "Highway service - bridge between the Public Internet and Sonr blockchain"
|
||||
commit_msg_template: "Brew cask update for {{ .ProjectName }} version {{ .Tag }}"
|
||||
directory: Casks
|
||||
repository:
|
||||
owner: sonr-io
|
||||
name: homebrew-tap
|
||||
branch: main
|
||||
token: "{{ .Env.GITHUB_TOKEN }}"
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: "prad@sonr.io"
|
||||
hooks:
|
||||
post:
|
||||
install: |
|
||||
if OS.mac?
|
||||
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/hway"]
|
||||
end
|
||||
|
||||
nfpms:
|
||||
- id: hway
|
||||
package_name: hway
|
||||
ids:
|
||||
- hway-linux-amd64
|
||||
- hway-linux-arm64
|
||||
file_name_template: "hway_{{ .Os }}_{{ .Arch }}{{ .ConventionalExtension }}"
|
||||
vendor: Sonr
|
||||
homepage: "https://sonr.io"
|
||||
maintainer: "Sonr <support@sonr.io>"
|
||||
description: "Hway is the bridge between the Public Internet and the Sonr blockchain."
|
||||
license: "GPL-3.0"
|
||||
formats:
|
||||
- rpm
|
||||
- deb
|
||||
- apk
|
||||
- archlinux
|
||||
contents:
|
||||
- src: README*
|
||||
dst: /usr/share/doc/hway
|
||||
bindir: /usr/bin
|
||||
section: net
|
||||
priority: optional
|
||||
|
||||
blobs:
|
||||
- provider: s3
|
||||
endpoint: https://eb37925850388bca807b7fab964c12bb.r2.cloudflarestorage.com
|
||||
bucket: releases
|
||||
region: auto
|
||||
directory: "hway/{{ .Tag }}"
|
||||
ids:
|
||||
- hway
|
||||
|
||||
release:
|
||||
disable: false
|
||||
github:
|
||||
owner: sonr-io
|
||||
name: sonr
|
||||
name_template: "{{.ProjectName}}/{{ .Tag }}"
|
||||
draft: false
|
||||
replace_existing_draft: false # Don't replace drafts
|
||||
replace_existing_artifacts: false # Append, don't replace
|
||||
mode: append # Explicitly set to append mode
|
||||
|
||||
checksum:
|
||||
name_template: 'hway_checksums.txt'
|
||||
|
||||
npms:
|
||||
- name: "@sonr.io/hway"
|
||||
ids:
|
||||
- hway
|
||||
description: "Highway service - bridge between the Public Internet and Sonr blockchain"
|
||||
homepage: "https://sonr.io"
|
||||
license: "GPL-3.0"
|
||||
author: "Sonr <support@sonr.io>"
|
||||
repository: "https://github.com/sonr-io/sonr"
|
||||
bugs: "https://github.com/sonr-io/sonr/issues"
|
||||
keywords:
|
||||
- highway
|
||||
- bridge
|
||||
- blockchain
|
||||
- sonr
|
||||
- service
|
||||
access: public
|
||||
format: tar.gz
|
||||
|
||||
snapshot:
|
||||
version_template: "{{ incpatch .Version }}-dev"
|
||||
|
||||
# Changelog configuration
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore:'
|
||||
@@ -1,5 +0,0 @@
|
||||
## hway/v0.0.3 (2025-10-04)
|
||||
|
||||
## hway/v0.0.2 (2025-10-04)
|
||||
|
||||
## hway/v0.0.1 (2025-10-03)
|
||||
@@ -1,77 +0,0 @@
|
||||
# Use build argument for base image to allow using pre-cached base
|
||||
ARG BASE_IMAGE=golang:1.24.7-alpine3.22
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Highway service build stage
|
||||
FROM ${BASE_IMAGE} AS builder
|
||||
SHELL ["/bin/sh", "-ecuxo", "pipefail"]
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
build-base \
|
||||
git \
|
||||
linux-headers \
|
||||
bash
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# Copy entire source code
|
||||
COPY . .
|
||||
|
||||
# Fix git ownership issue
|
||||
RUN git config --global --add safe.directory /code
|
||||
|
||||
# Download Go modules
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download
|
||||
|
||||
# Build Highway binary
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
set -eux; \
|
||||
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev"); \
|
||||
COMMIT=$(git log -1 --format='%H' 2>/dev/null || echo "unknown"); \
|
||||
CGO_ENABLED=1 GOOS=linux \
|
||||
go build \
|
||||
-mod=readonly \
|
||||
-ldflags "-X main.Version=${VERSION} \
|
||||
-X main.Commit=${COMMIT} \
|
||||
-w -s" \
|
||||
-buildvcs=false \
|
||||
-trimpath \
|
||||
-o /code/build/hway \
|
||||
./cmd/hway
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Highway runtime image
|
||||
FROM alpine:3.17
|
||||
|
||||
LABEL org.opencontainers.image.title="Sonr Highway Service"
|
||||
LABEL org.opencontainers.image.source="https://github.com/sonr-io/sonr"
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /code/build/hway /usr/bin/hway
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache ca-certificates wget
|
||||
|
||||
# Create non-root user
|
||||
RUN adduser -D -u 1000 highway
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /home/highway
|
||||
|
||||
# Switch to non-root user
|
||||
USER highway
|
||||
|
||||
# Health check endpoint
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --spider -q http://localhost:8090/health || exit 1
|
||||
|
||||
# Expose Highway port
|
||||
EXPOSE 8090
|
||||
|
||||
# Set default command
|
||||
ENTRYPOINT ["/usr/bin/hway"]
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Version and build information
|
||||
GIT_ROOT := $(shell git rev-parse --show-toplevel)
|
||||
VERSION := $(shell echo $(shell git describe --tags 2>/dev/null || echo "dev") | sed 's/^v//')
|
||||
COMMIT := $(shell git log -1 --format='%H')
|
||||
|
||||
BUMP_LEVEL := PATCH
|
||||
|
||||
# Build configuration
|
||||
BUILD_FLAGS := -trimpath
|
||||
|
||||
# Linker flags for version info
|
||||
ldflags = -X main.Version=$(VERSION) \
|
||||
-X main.Commit=$(COMMIT) \
|
||||
-s -w
|
||||
|
||||
BUILD_FLAGS += -ldflags '$(ldflags)'
|
||||
HWAY_OUT := $(GIT_ROOT)/build/hway
|
||||
|
||||
.PHONY: all build install clean test version help docker
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
$(error hway server not supported on Windows)
|
||||
else
|
||||
@echo "Building Highway service..."
|
||||
@go build -mod=readonly $(BUILD_FLAGS) -o $(HWAY_OUT) .
|
||||
@echo "Binary built: ../../build/hway"
|
||||
endif
|
||||
|
||||
install:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
$(error hway server not supported on Windows)
|
||||
else
|
||||
@echo "Installing Highway service..."
|
||||
@go install -mod=readonly $(BUILD_FLAGS) .
|
||||
@echo "Binary installed to $(GOPATH)/bin/hway"
|
||||
endif
|
||||
|
||||
tidy:
|
||||
@echo "Tidying Go module..."
|
||||
@go mod tidy
|
||||
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
@rm -f $(HWAY_OUT)
|
||||
@echo "Clean complete"
|
||||
|
||||
test:
|
||||
@echo "Running Highway service tests..."
|
||||
@go test -v -race ./...
|
||||
|
||||
release:
|
||||
@echo "Creating hway release..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/hway/.cz.toml --no-raise 6,21 bump --yes --increment PATCH
|
||||
|
||||
snapshot:
|
||||
@echo "Dry-Run Bumping Highway version..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/hway/.cz.toml bump --yes --no-verify --dry-run --increment PATCH
|
||||
@echo "Creating hway snapshots for all platforms..."
|
||||
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean -f cmd/hway/.goreleaser.yml
|
||||
|
||||
# Docker build targets
|
||||
docker:
|
||||
@echo "Building Highway Docker image..."
|
||||
@docker build -f Dockerfile -t onsonr/hway:latest -t ghcr.io/sonr-io/hway:latest ../..
|
||||
@echo "Docker image built and tagged:"
|
||||
@echo " - onsonr/hway:latest"
|
||||
@echo " - ghcr.io/sonr-io/hway:latest"
|
||||
|
||||
docker-local:
|
||||
@echo "Building Highway Docker image with local context..."
|
||||
@docker build -f Dockerfile -t onsonr/hway:local -t ghcr.io/sonr-io/hway:local ../..
|
||||
@echo "Docker image built and tagged:"
|
||||
@echo " - onsonr/hway:local"
|
||||
@echo " - ghcr.io/sonr-io/hway:local"
|
||||
|
||||
version:
|
||||
@echo "Highway Service"
|
||||
@echo "==============="
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "Commit: $(COMMIT)"
|
||||
|
||||
help:
|
||||
@echo "Highway Service Makefile"
|
||||
@echo "========================"
|
||||
@echo ""
|
||||
@echo "Highway is an Asynq-based task processing service for vault operations,"
|
||||
@echo "using Redis-backed job queues and Proto.Actor framework for concurrency."
|
||||
@echo ""
|
||||
@echo "Available targets:"
|
||||
@echo " build - Build Highway binary (default)"
|
||||
@echo " install - Install Highway to GOPATH/bin"
|
||||
@echo " docker - Build Docker image (onsonr/hway:latest)"
|
||||
@echo " clean - Remove build artifacts"
|
||||
@echo " test - Run Highway tests"
|
||||
@echo " version - Display version information"
|
||||
@echo " help - Show this help message"
|
||||
@echo ""
|
||||
@echo "Requirements:"
|
||||
@echo " - Redis server running on 127.0.0.1:6379"
|
||||
@echo " - IPFS nodes for vault operations"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make build # Build the Highway service"
|
||||
@echo " make test # Run tests"
|
||||
@echo " make install # Install to GOPATH/bin"
|
||||
Binary file not shown.
-312
@@ -1,312 +0,0 @@
|
||||
module hway
|
||||
|
||||
go 1.24.7
|
||||
|
||||
// overrides
|
||||
replace (
|
||||
cosmossdk.io/core => cosmossdk.io/core v0.11.0
|
||||
cosmossdk.io/store => github.com/evmos/cosmos-sdk/store v0.0.0-20240718141609-414cbd051fbe
|
||||
github.com/CosmWasm/wasmd => github.com/rollchains/wasmd v0.50.0-evm
|
||||
|
||||
github.com/cosmos/cosmos-sdk => github.com/strangelove-ventures/cosmos-sdk v0.0.0-20250317212103-0767f8c5b1e5
|
||||
github.com/cosmos/evm => github.com/strangelove-ventures/cosmos-evm v0.2.0
|
||||
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc2
|
||||
github.com/sonr-io/sonr => ../../
|
||||
github.com/sonr-io/sonr/crypto => ../../crypto
|
||||
github.com/spf13/viper => github.com/spf13/viper v1.17.0 // v1.18+ breaks app overrides
|
||||
nhooyr.io/websocket => nhooyr.io/websocket v1.8.17
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
|
||||
// Fix btcec version for evmos compatibility
|
||||
github.com/btcsuite/btcd/btcec/v2 => github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||
// dgrijalva/jwt-go is deprecated and doesn't receive security updates.
|
||||
// See: https://github.com/cosmos/cosmos-sdk/issues/13134
|
||||
github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
||||
// See: https://github.com/cosmos/cosmos-sdk/issues/10409
|
||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.8.1
|
||||
|
||||
// pin version! 126854af5e6d has issues with the store so that queries fail
|
||||
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
)
|
||||
|
||||
require github.com/sonr-io/sonr v0.0.0-00010101000000-000000000000
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.6 // indirect
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/core v0.12.0 // indirect
|
||||
cosmossdk.io/depinject v1.1.0 // indirect
|
||||
cosmossdk.io/errors v1.0.1 // indirect
|
||||
cosmossdk.io/math v1.5.0 // indirect
|
||||
cosmossdk.io/orm v1.0.0-beta.3 // indirect
|
||||
cosmossdk.io/store v1.1.1 // indirect
|
||||
cosmossdk.io/x/tx v0.13.7 // indirect
|
||||
github.com/Oudwins/zog v0.21.6 // indirect
|
||||
github.com/asynkron/protoactor-go v0.0.0-20240822202345-3c0e61ca19c9 // indirect
|
||||
github.com/biter777/countries v1.7.5 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect
|
||||
github.com/cosmos/gogoproto v1.7.0 // indirect
|
||||
github.com/extism/go-sdk v1.7.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/ipfs/boxo v0.32.0 // indirect
|
||||
github.com/ipfs/kubo v0.35.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.4 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/sonr-io/sonr/crypto v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/log v1.5.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e // indirect
|
||||
github.com/Workiva/go-datastructures v1.1.3 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
|
||||
github.com/bwesterb/go-ristretto v1.2.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.6 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/cockroachdb/crlib v0.0.0-20241015224233-894974b3ad94 // indirect
|
||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v1.1.2 // indirect
|
||||
github.com/cockroachdb/pebble/v2 v2.0.3 // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft v0.38.17 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/consensys/gnark-crypto v0.19.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.1.1 // indirect
|
||||
github.com/cosmos/cosmos-sdk v0.53.4 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect
|
||||
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
||||
github.com/flynn/noise v1.1.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gammazero/deque v1.0.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v23.5.26+incompatible // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.3 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/hibiken/asynq v0.25.1 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-bitfield v1.1.0 // indirect
|
||||
github.com/ipfs/go-block-format v0.2.1 // indirect
|
||||
github.com/ipfs/go-cid v0.5.0 // indirect
|
||||
github.com/ipfs/go-datastore v0.8.2 // indirect
|
||||
github.com/ipfs/go-ds-measure v0.2.2 // indirect
|
||||
github.com/ipfs/go-fs-lock v0.1.1 // indirect
|
||||
github.com/ipfs/go-ipfs-cmds v0.14.1 // indirect
|
||||
github.com/ipfs/go-ipld-cbor v0.2.0 // indirect
|
||||
github.com/ipfs/go-ipld-format v0.6.1 // indirect
|
||||
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.6.0 // indirect
|
||||
github.com/ipfs/go-metrics-interface v0.3.0 // indirect
|
||||
github.com/ipfs/go-unixfsnode v1.10.1 // indirect
|
||||
github.com/ipld/go-car/v2 v2.14.3 // indirect
|
||||
github.com/ipld/go-codec-dagpb v1.7.0 // indirect
|
||||
github.com/ipld/go-ipld-prime v0.21.0 // indirect
|
||||
github.com/ipshipyard/p2p-forge v0.5.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/koron/go-ssdp v0.0.6 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/labstack/echo-jwt/v4 v4.3.1 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p v0.43.0 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.33.1 // indirect
|
||||
github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect
|
||||
github.com/libp2p/go-libp2p-record v0.3.1 // indirect
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect
|
||||
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.2 // indirect
|
||||
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||
github.com/linxGnu/grocksdb v1.9.8 // indirect
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
|
||||
github.com/lmittmann/tint v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mholt/acmez/v3 v3.0.0 // indirect
|
||||
github.com/miekg/dns v1.1.66 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.16.0 // indirect
|
||||
github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.9.1 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-multistream v0.6.1 // indirect
|
||||
github.com/multiformats/go-varint v0.1.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/onsi/gomega v1.36.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/orcaman/concurrent-map v1.0.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/pion/datachannel v1.5.10 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.12 // indirect
|
||||
github.com/pion/dtls/v3 v3.0.6 // indirect
|
||||
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||
github.com/pion/interceptor v0.1.40 // indirect
|
||||
github.com/pion/logging v0.2.3 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.15 // indirect
|
||||
github.com/pion/rtp v1.8.19 // indirect
|
||||
github.com/pion/sctp v1.8.39 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.13 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||
github.com/pion/stun v0.6.1 // indirect
|
||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||
github.com/pion/transport/v2 v2.2.10 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/turn/v4 v4.0.2 // indirect
|
||||
github.com/pion/webrtc/v4 v4.1.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/polydawn/refmt v0.89.0 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/quic-go/webtransport-go v0.9.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.11.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/samber/lo v1.47.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/smarty/assertions v1.15.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
|
||||
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect
|
||||
github.com/whyrusleeping/cbor-gen v0.1.2 // indirect
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/dig v1.19.0 // indirect
|
||||
go.uber.org/fx v1.24.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
gonum.org/v1/gonum v0.16.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||
)
|
||||
-1382
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
// Package main provides the Highway service, a UCAN-based task processing service
|
||||
// that acts as a bridge proxy for MPC operations and decentralized identity management.
|
||||
//
|
||||
// The service processes asynchronous UCAN (User-Controlled Authorization Networks) tasks
|
||||
// including token creation, delegation, signing, verification, and DID generation.
|
||||
// It serves as a proxy between the bridge handlers and the underlying blockchain operations.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/sonr-io/sonr/bridge"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create and configure the Highway service
|
||||
service := bridge.NewHighwayService()
|
||||
defer service.Shutdown()
|
||||
|
||||
// Start the service and block until shutdown
|
||||
service.Start()
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package main
|
||||
|
||||
// Version is set by commitizen during release process
|
||||
var Version = "dev"
|
||||
@@ -1,18 +0,0 @@
|
||||
[tool.commitizen]
|
||||
name = "cz_customize"
|
||||
tag_format = "motr/v$version"
|
||||
ignored_tag_formats = ["*/v${version}", "v${version}"]
|
||||
version_scheme = "semver"
|
||||
version_provider = "scm"
|
||||
update_changelog_on_bump = true
|
||||
changelog_file = "CHANGELOG.md"
|
||||
major_version_zero = true
|
||||
annotated_tag = true
|
||||
pre_bump_hooks = ["bash scripts/hook-bump-pre.sh"]
|
||||
post_bump_hooks = ["goreleaser release --clean -f cmd/motr/.goreleaser.yml"]
|
||||
|
||||
[tool.commitizen.customize]
|
||||
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
|
||||
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
|
||||
default_bump = "PATCH"
|
||||
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(motr\\)(!)?:"
|
||||
@@ -1,75 +0,0 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
|
||||
---
|
||||
version: 2
|
||||
dist: dist/motr
|
||||
monorepo:
|
||||
tag_prefix: motr/
|
||||
dir: cmd/motr
|
||||
|
||||
project_name: motr
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
|
||||
builds:
|
||||
- id: motr-wasm
|
||||
main: .
|
||||
binary: motr
|
||||
no_unique_dist_dir: true
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- js
|
||||
goarch:
|
||||
- wasm
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.version={{.Version}}
|
||||
- -X main.commit={{.Commit}}
|
||||
- -X main.date={{.Date}}
|
||||
hooks:
|
||||
post:
|
||||
- cp dist/motr/motr.wasm packages/es/src/worker/app.wasm
|
||||
archives:
|
||||
- id: motr-wasm-archive
|
||||
name_template: "motr_wasm_{{ .Version }}"
|
||||
formats: ["binary"]
|
||||
wrap_in_directory: true
|
||||
|
||||
blobs:
|
||||
- provider: s3
|
||||
endpoint: https://eb37925850388bca807b7fab964c12bb.r2.cloudflarestorage.com
|
||||
bucket: releases
|
||||
region: auto
|
||||
directory: "motr/{{ .Tag }}"
|
||||
|
||||
release:
|
||||
disable: false
|
||||
github:
|
||||
owner: sonr-io
|
||||
name: sonr
|
||||
name_template: "{{.ProjectName}}/{{ .Tag }}"
|
||||
draft: false
|
||||
replace_existing_draft: false # Don't replace drafts
|
||||
replace_existing_artifacts: false # Append, don't replace
|
||||
mode: append # Explicitly set to append mode
|
||||
|
||||
checksum:
|
||||
name_template: "motr_checksums.txt"
|
||||
|
||||
snapshot:
|
||||
version_template: "{{ incpatch .Version }}-dev"
|
||||
|
||||
# Changelog configuration
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- "^chore:"
|
||||
@@ -1,5 +0,0 @@
|
||||
## motr/v0.0.3 (2025-10-04)
|
||||
|
||||
## motr/v0.0.2 (2025-10-04)
|
||||
|
||||
## motr/v0.0.1 (2025-10-03)
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Output configuration - outputs to ES package for bundling
|
||||
GIT_ROOT := $(shell git rev-parse --show-toplevel)
|
||||
ES_PACKAGE_DIR := $(GIT_ROOT)/packages/es/src/worker
|
||||
WASM_FILE := app.wasm
|
||||
JS_FILE := wasm_exec.js
|
||||
OUTPUT_PATH := $(ES_PACKAGE_DIR)/$(WASM_FILE)
|
||||
JS_PATH := $(ES_PACKAGE_DIR)/$(JS_FILE)
|
||||
|
||||
# Build configuration for WASM
|
||||
GOOS := js
|
||||
GOARCH := wasm
|
||||
CGO_ENABLED := 0
|
||||
|
||||
# Version information
|
||||
VERSION := $(shell echo $(shell git describe --tags 2>/dev/null || echo "dev") | sed 's/^v//')
|
||||
COMMIT := $(shell git log -1 --format='%H')
|
||||
|
||||
# Go installation paths
|
||||
GOROOT := $(shell go env GOROOT)
|
||||
WASM_EXEC_SOURCE := $(GOROOT)/misc/wasm/wasm_exec.js
|
||||
|
||||
# Build flags
|
||||
LDFLAGS := -s -w
|
||||
BUILD_FLAGS := -ldflags="$(LDFLAGS)" -trimpath
|
||||
|
||||
.PHONY: all build clean test verify help version runtime tidy
|
||||
|
||||
all: build
|
||||
|
||||
build: clean-output runtime
|
||||
@echo "Building Motor WASM module for ES package..."
|
||||
@echo "Target: $(OUTPUT_PATH)"
|
||||
@mkdir -p $(ES_PACKAGE_DIR)
|
||||
@GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) go build $(BUILD_FLAGS) -o $(OUTPUT_PATH) .
|
||||
@echo "✅ Motor WASM module built successfully"
|
||||
@echo "Output: $(OUTPUT_PATH)"
|
||||
@ls -lh $(OUTPUT_PATH) | awk '{print "Size: " $$5}'
|
||||
@$(MAKE) runtime
|
||||
|
||||
runtime:
|
||||
@echo "Copying WASM runtime..."
|
||||
@if [ -f "$(WASM_EXEC_SOURCE)" ]; then \
|
||||
cp "$(WASM_EXEC_SOURCE)" "$(JS_PATH)"; \
|
||||
echo "✅ WASM runtime copied to $(JS_PATH)"; \
|
||||
else \
|
||||
echo "⚠️ WASM runtime not found at $(WASM_EXEC_SOURCE)"; \
|
||||
echo "You may need to manually copy wasm_exec.js"; \
|
||||
fi
|
||||
|
||||
clean-output:
|
||||
@echo "Cleaning previous builds..."
|
||||
@rm -f $(OUTPUT_PATH)
|
||||
@rm -f $(JS_PATH)
|
||||
@mkdir -p $(ES_PACKAGE_DIR)
|
||||
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
@rm -f $(OUTPUT_PATH)
|
||||
@rm -f $(JS_PATH)
|
||||
@echo "✅ Clean complete"
|
||||
|
||||
release:
|
||||
@echo "Creating motr release..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/motr/.cz.toml --no-raise 6,21 bump --yes --increment PATCH
|
||||
|
||||
snapshot:
|
||||
@echo "Dry-Run Bumping Motor version..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/motr/.cz.toml bump --yes --no-verify --dry-run --increment PATCH
|
||||
@echo "Creating motr snapshots for all platforms..."
|
||||
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean -f cmd/motr/.goreleaser.yml
|
||||
|
||||
tidy:
|
||||
@echo "Tidying Motor module..."
|
||||
@go mod tidy
|
||||
@echo "✅ Tidy complete"
|
||||
|
||||
test:
|
||||
@echo "Running Motor tests..."
|
||||
@go test -v ./...
|
||||
|
||||
verify: build
|
||||
@echo "Verifying WASM module..."
|
||||
@if [ -f "$(OUTPUT_PATH)" ]; then \
|
||||
file "$(OUTPUT_PATH)"; \
|
||||
echo "✅ WASM file exists"; \
|
||||
else \
|
||||
echo "❌ WASM file not found"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -f "$(JS_PATH)" ]; then \
|
||||
echo "✅ Runtime file exists"; \
|
||||
else \
|
||||
echo "⚠️ Runtime file not found"; \
|
||||
fi
|
||||
@if command -v wasm-validate >/dev/null 2>&1; then \
|
||||
if wasm-validate "$(OUTPUT_PATH)"; then \
|
||||
echo "✅ WASM module is valid"; \
|
||||
else \
|
||||
echo "❌ WASM module validation failed"; \
|
||||
exit 1; \
|
||||
fi \
|
||||
else \
|
||||
echo "⚠️ wasm-validate not available, skipping validation"; \
|
||||
fi
|
||||
@echo "✅ Verification complete"
|
||||
@echo ""
|
||||
@echo "The WASM module has been built in the ES package at:"
|
||||
@echo " $(OUTPUT_PATH)"
|
||||
@echo ""
|
||||
@echo "The ES package will bundle and distribute this via jsDelivr"
|
||||
|
||||
version:
|
||||
@echo "Motor WASM Service Worker"
|
||||
@echo "========================="
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "Commit: $(COMMIT)"
|
||||
@echo "Target OS: $(GOOS)"
|
||||
@echo "Target Arch: $(GOARCH)"
|
||||
@echo "Output: $(OUTPUT_PATH)"
|
||||
|
||||
help:
|
||||
@echo "Motor WASM Module Makefile"
|
||||
@echo "=========================="
|
||||
@echo ""
|
||||
@echo "Motor provides WebAssembly-based DWN and Wallet operations"
|
||||
@echo "for the @sonr.io/es package to distribute via jsDelivr."
|
||||
@echo ""
|
||||
@echo "Available targets:"
|
||||
@echo " build - Build Motor WASM module (default)"
|
||||
@echo " clean - Remove all build artifacts"
|
||||
@echo " test - Run Motor tests"
|
||||
@echo " tidy - Tidy Go module dependencies"
|
||||
@echo " verify - Build and validate WASM module"
|
||||
@echo " version - Display version information"
|
||||
@echo " help - Show this help message"
|
||||
@echo ""
|
||||
@echo "Build components (called by build):"
|
||||
@echo " runtime - Copy WASM runtime (wasm_exec.js)"
|
||||
@echo ""
|
||||
@echo "Output location:"
|
||||
@echo " ES Package: $(ES_PACKAGE_DIR)/"
|
||||
@echo " WASM File: $(OUTPUT_PATH)"
|
||||
@echo ""
|
||||
@echo "Integration:"
|
||||
@echo " The WASM module is built directly into the ES package plugins"
|
||||
@echo " directory for bundling and CDN distribution. The TypeScript"
|
||||
@echo " client in @sonr.io/es/plugins/motor handles service worker management."
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make build # Build WASM module into ES package"
|
||||
@echo " make verify # Build and validate the module"
|
||||
@echo " make clean # Remove artifacts"
|
||||
@@ -1,385 +0,0 @@
|
||||
# Motor WASM Service Worker - Payment Gateway & OIDC Authorization
|
||||
|
||||
Motor is a WebAssembly-based HTTP server that runs as a Service Worker in the browser, providing secure payment processing and OpenID Connect (OIDC) authorization without requiring backend infrastructure.
|
||||
|
||||
## Overview
|
||||
|
||||
Motor implements a comprehensive payment gateway and identity provider that runs entirely in the browser:
|
||||
|
||||
1. **Payment Gateway**: W3C Payment Handler API compliant payment processing with PCI DSS compliance
|
||||
2. **OIDC Authorization**: Complete OpenID Connect provider with JWT token management
|
||||
3. **Service Worker**: Runs as a browser service worker using go-wasm-http-server
|
||||
|
||||
## Features
|
||||
|
||||
### Payment Gateway (W3C Payment Handler API)
|
||||
- ✅ Process payment transactions securely
|
||||
- ✅ PCI DSS compliant card tokenization
|
||||
- ✅ Card validation (Luhn algorithm, CVV, expiry)
|
||||
- ✅ Transaction signing with HMAC-SHA256
|
||||
- ✅ AES-256-GCM encryption for sensitive data
|
||||
- ✅ Payment method validation
|
||||
- ✅ Refund processing
|
||||
- ✅ Comprehensive audit logging
|
||||
|
||||
### OIDC Authorization
|
||||
- ✅ Discovery endpoint (`.well-known/openid-configuration`)
|
||||
- ✅ Authorization endpoint with PKCE support
|
||||
- ✅ Token endpoint with JWT generation
|
||||
- ✅ UserInfo endpoint
|
||||
- ✅ JWKS endpoint for key rotation
|
||||
- ✅ RS256 JWT signing
|
||||
- ✅ Refresh token support
|
||||
|
||||
### Security Features
|
||||
- ✅ Rate limiting (100 requests/minute per client)
|
||||
- ✅ Origin validation
|
||||
- ✅ Security headers (CSP, X-Frame-Options, etc.)
|
||||
- ✅ CORS configuration
|
||||
- ✅ Secure token generation
|
||||
- ✅ Card number masking
|
||||
- ✅ Sensitive data sanitization
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Payment Gateway Endpoints
|
||||
|
||||
#### Process Payment
|
||||
```http
|
||||
POST /api/payment/process
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"method": "card",
|
||||
"amount": 100.00,
|
||||
"currency": "USD",
|
||||
"card_number": "4111111111111111",
|
||||
"cvv": "123",
|
||||
"expiry_month": 12,
|
||||
"expiry_year": 2025,
|
||||
"billing_address": {
|
||||
"line1": "123 Main St",
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"postal_code": "94105",
|
||||
"country": "US"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Validate Payment Method
|
||||
```http
|
||||
POST /api/payment/validate
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"method": "card",
|
||||
"card_number": "4111111111111111",
|
||||
"cvv": "123",
|
||||
"expiry_month": 12,
|
||||
"expiry_year": 2025
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Payment Status
|
||||
```http
|
||||
GET /api/payment/status/:id
|
||||
```
|
||||
|
||||
#### Process Refund
|
||||
```http
|
||||
POST /api/payment/refund
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"payment_id": "pay_abc123",
|
||||
"amount": 50.00,
|
||||
"reason": "Customer request"
|
||||
}
|
||||
```
|
||||
|
||||
#### W3C Payment Handler API
|
||||
```http
|
||||
GET /payment/instruments
|
||||
POST /payment/canmakepayment
|
||||
POST /payment/paymentrequest
|
||||
```
|
||||
|
||||
### OIDC Endpoints
|
||||
|
||||
#### Discovery
|
||||
```http
|
||||
GET /.well-known/openid-configuration
|
||||
```
|
||||
|
||||
#### Authorization
|
||||
```http
|
||||
GET /authorize?client_id=CLIENT_ID&redirect_uri=URI&response_type=code&scope=openid%20profile
|
||||
```
|
||||
|
||||
#### Token Exchange
|
||||
```http
|
||||
POST /token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=authorization_code&code=AUTH_CODE&client_id=CLIENT_ID
|
||||
```
|
||||
|
||||
#### UserInfo
|
||||
```http
|
||||
GET /userinfo
|
||||
Authorization: Bearer ACCESS_TOKEN
|
||||
```
|
||||
|
||||
#### JWKS
|
||||
```http
|
||||
GET /.well-known/jwks.json
|
||||
```
|
||||
|
||||
### Health & Monitoring
|
||||
|
||||
```http
|
||||
GET /health
|
||||
GET /status
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
### Using Make
|
||||
```bash
|
||||
# Build Motor WASM module
|
||||
make motr-wasm
|
||||
|
||||
# Or build directly
|
||||
cd cmd/motr
|
||||
GOOS=js GOARCH=wasm go build -o ../../packages/es/src/plugins/motor/motor.wasm .
|
||||
```
|
||||
|
||||
### Build Output
|
||||
The WASM module is built to: `packages/es/src/plugins/motor/motor.wasm`
|
||||
|
||||
## Integration
|
||||
|
||||
### Service Worker Registration
|
||||
|
||||
```javascript
|
||||
// motor-worker.js
|
||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.4/misc/wasm/wasm_exec.js');
|
||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.2.1/sw.js');
|
||||
|
||||
// Register Motor WASM as HTTP listener
|
||||
registerWasmHTTPListener('motor.wasm', {
|
||||
base: '/api'
|
||||
});
|
||||
```
|
||||
|
||||
### TypeScript Client Usage
|
||||
|
||||
```typescript
|
||||
import { PaymentGatewayClient, OIDCClient } from '@sonr.io/es/plugins/motor';
|
||||
|
||||
// Initialize clients
|
||||
const payment = new PaymentGatewayClient('https://localhost:3000');
|
||||
const oidc = new OIDCClient('https://localhost:3000');
|
||||
|
||||
// Process a payment
|
||||
const result = await payment.processPayment({
|
||||
method: 'card',
|
||||
amount: 100.00,
|
||||
currency: 'USD',
|
||||
card_number: '4111111111111111',
|
||||
cvv: '123',
|
||||
expiry_month: 12,
|
||||
expiry_year: 2025
|
||||
});
|
||||
|
||||
// OIDC authorization flow
|
||||
const authUrl = await oidc.buildAuthorizationUrl({
|
||||
client_id: 'my-app',
|
||||
redirect_uri: 'https://myapp.com/callback',
|
||||
scope: 'openid profile email'
|
||||
});
|
||||
|
||||
// Exchange authorization code for tokens
|
||||
const tokens = await oidc.exchangeCode('auth_code_here', 'code_verifier');
|
||||
```
|
||||
|
||||
## Security Implementation
|
||||
|
||||
### PCI DSS Compliance
|
||||
- **Tokenization**: Cards are immediately tokenized, raw data never stored
|
||||
- **Encryption**: AES-256-GCM for all sensitive data at rest
|
||||
- **Masking**: Card numbers always masked except last 4 digits
|
||||
- **Audit Logging**: Complete audit trail for compliance
|
||||
- **CVV Handling**: CVV never stored, only validated
|
||||
|
||||
### Transaction Security
|
||||
- **Signing**: HMAC-SHA256 signatures on all transactions
|
||||
- **Verification**: Signature verification before processing
|
||||
- **Tamper Detection**: Any modification invalidates transaction
|
||||
- **Idempotency**: Duplicate transaction prevention
|
||||
|
||||
### Authentication Security
|
||||
- **JWT Signing**: RS256 with 2048-bit RSA keys
|
||||
- **PKCE**: Proof Key for Code Exchange for authorization flow
|
||||
- **Token Expiration**: Configurable expiration (default 1 hour)
|
||||
- **Refresh Tokens**: Secure refresh token rotation
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
# Run unit tests (without WASM constraints)
|
||||
go test ./cmd/motr/...
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
```bash
|
||||
# Build WASM first
|
||||
make motr-wasm
|
||||
|
||||
# Run integration tests
|
||||
cd cmd/motr
|
||||
GOOS=js GOARCH=wasm go test -v
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
- ✅ Payment processing flows
|
||||
- ✅ Card validation (Luhn, CVV, expiry)
|
||||
- ✅ Tokenization and encryption
|
||||
- ✅ Transaction signing/verification
|
||||
- ✅ OIDC discovery and flows
|
||||
- ✅ JWT generation/validation
|
||||
- ✅ Rate limiting
|
||||
- ✅ Security headers
|
||||
- ✅ PCI compliance features
|
||||
|
||||
## Performance
|
||||
|
||||
### Bundle Size
|
||||
- WASM module: ~3-4MB (production build)
|
||||
- Service Worker: ~10KB
|
||||
- TypeScript client: ~25KB (minified)
|
||||
|
||||
### Optimization
|
||||
- Built with `-ldflags="-s -w"` for size reduction
|
||||
- Gzip compression reduces transfer to ~1MB
|
||||
- Lazy loading recommended for optimal performance
|
||||
|
||||
### Benchmarks
|
||||
- Payment processing: <100ms average
|
||||
- Token generation: <50ms
|
||||
- Card validation: <10ms
|
||||
- Encryption/decryption: <20ms
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
| Feature | Chrome | Firefox | Safari | Edge |
|
||||
|---------|--------|---------|--------|------|
|
||||
| Service Workers | 45+ | 44+ | 11.1+ | 17+ |
|
||||
| WebAssembly | 57+ | 52+ | 11+ | 16+ |
|
||||
| Payment Handler | 68+ | - | - | 79+ |
|
||||
| Full Support | 68+ | 52+* | 11.1+* | 79+ |
|
||||
|
||||
*Payment Handler API has limited support
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
```javascript
|
||||
// Configure in service worker
|
||||
const config = {
|
||||
issuer: 'https://motor.sonr.io',
|
||||
rateLimit: 100, // requests per minute
|
||||
rateWindow: 60000, // milliseconds
|
||||
tokenExpiry: 3600, // seconds
|
||||
allowedOrigins: ['https://localhost:3000']
|
||||
};
|
||||
```
|
||||
|
||||
### Security Settings
|
||||
- Rate limiting: Configurable per-client limits
|
||||
- CORS: Configurable allowed origins
|
||||
- CSP: Customizable content security policy
|
||||
- Token expiry: Adjustable for different use cases
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
- Go 1.21+ (1.23+ recommended)
|
||||
- Modern browser with Service Worker support
|
||||
- HTTPS or localhost (Service Workers requirement)
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
# Build WASM module
|
||||
make motr-wasm
|
||||
|
||||
# Start local server (example)
|
||||
cd packages/es/src/plugins/motor
|
||||
python3 -m http.server 8080 --bind localhost
|
||||
|
||||
# Access at https://localhost:8080
|
||||
```
|
||||
|
||||
### Debugging
|
||||
- Browser DevTools: Network tab for API inspection
|
||||
- Service Worker: Application tab for SW debugging
|
||||
- Console: WASM logs and errors
|
||||
- Payment Handler: chrome://settings/content/paymentHandler
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Best Practices
|
||||
1. **HTTPS Required**: Service Workers only work over HTTPS
|
||||
2. **Cache Strategy**: Implement proper cache headers
|
||||
3. **Error Handling**: Comprehensive error logging
|
||||
4. **Monitoring**: Track payment success rates
|
||||
5. **Compliance**: Regular PCI DSS audits
|
||||
|
||||
### Deployment Checklist
|
||||
- [ ] Configure production issuer URL
|
||||
- [ ] Set appropriate rate limits
|
||||
- [ ] Configure allowed origins
|
||||
- [ ] Enable production encryption keys
|
||||
- [ ] Set up monitoring and alerting
|
||||
- [ ] Configure backup payment processors
|
||||
- [ ] Implement fraud detection rules
|
||||
- [ ] Schedule security audits
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Service Worker Not Registering
|
||||
- Ensure HTTPS or localhost
|
||||
- Check browser compatibility
|
||||
- Verify WASM file path
|
||||
|
||||
#### Payment Processing Errors
|
||||
- Validate card details format
|
||||
- Check rate limiting
|
||||
- Verify origin is allowed
|
||||
|
||||
#### OIDC Flow Issues
|
||||
- Ensure redirect URI matches
|
||||
- Check PKCE implementation
|
||||
- Verify token expiration
|
||||
|
||||
### Debug Mode
|
||||
Enable debug logging in the service worker:
|
||||
```javascript
|
||||
// motor-worker.js
|
||||
const DEBUG = true;
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This implementation is part of the Sonr project and follows the same license terms.
|
||||
|
||||
## Support
|
||||
|
||||
For issues, questions, or contributions:
|
||||
- GitHub Issues: https://github.com/sonr-io/sonr/issues
|
||||
- Documentation: https://docs.sonr.io
|
||||
- Security: security@sonr.io (for security vulnerabilities)
|
||||
@@ -1,10 +0,0 @@
|
||||
module motr
|
||||
|
||||
go 1.24.7
|
||||
|
||||
require github.com/go-sonr/wasm-http-server/v3 v3.0.0
|
||||
|
||||
require (
|
||||
github.com/hack-pad/safejs v0.1.1 // indirect
|
||||
github.com/nlepage/go-js-promise v1.0.0 // indirect
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
github.com/go-sonr/wasm-http-server/v3 v3.0.0 h1:DY/XaJD0jfKlvpVlOTqyU5b+SRVOulR5+zOye0LK0o0=
|
||||
github.com/go-sonr/wasm-http-server/v3 v3.0.0/go.mod h1:97QCYR5OlAEWeKeeIKCMZqCOIHqJakyTIFu0sbwDSJ8=
|
||||
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
||||
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||
github.com/nlepage/go-js-promise v1.0.0 h1:K7OmJ3+0BgWJ2LfXchg2sI6RDr7AW/KWR8182epFwGQ=
|
||||
github.com/nlepage/go-js-promise v1.0.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo=
|
||||
@@ -1,446 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Health & Status Handlers
|
||||
|
||||
// handleHealth returns service health status
|
||||
func handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"status": "healthy",
|
||||
"service": "motor-gateway",
|
||||
"timestamp": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
// handleStatus returns detailed service status
|
||||
func handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"status": "operational",
|
||||
"version": "1.0.0",
|
||||
"services": map[string]string{
|
||||
"payment_gateway": "active",
|
||||
"oidc_provider": "active",
|
||||
},
|
||||
"uptime": time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
// W3C Payment Handler API Handlers
|
||||
|
||||
// handlePaymentInstruments returns available payment instruments
|
||||
func handlePaymentInstruments(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
instruments := paymentHandler.GetInstruments()
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"instruments": instruments,
|
||||
})
|
||||
}
|
||||
|
||||
// handleCanMakePayment checks if payment can be made
|
||||
func handleCanMakePayment(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
MethodData []PaymentMethod `json:"methodData"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
canMakePayment := paymentHandler.CanMakePayment(req.MethodData)
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"canMakePayment": canMakePayment,
|
||||
})
|
||||
}
|
||||
|
||||
// handlePaymentRequest handles W3C PaymentRequestEvent
|
||||
func handlePaymentRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse payment request event
|
||||
var reqData json.RawMessage
|
||||
if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
paymentReq, err := SerializePaymentRequest(reqData)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid payment request")
|
||||
return
|
||||
}
|
||||
|
||||
// Process payment request
|
||||
tx, err := paymentHandler.ProcessPayment(paymentReq)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Payment processing failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Return payment response
|
||||
if tx.Response != nil {
|
||||
writeJSON(w, http.StatusOK, tx.Response)
|
||||
} else {
|
||||
writeJSON(w, http.StatusAccepted, map[string]interface{}{
|
||||
"transactionId": tx.ID,
|
||||
"status": tx.Status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Payment Gateway Handlers
|
||||
|
||||
// handlePaymentProcess processes a payment transaction using W3C Payment Handler API
|
||||
func handlePaymentProcess(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse payment request
|
||||
var reqData json.RawMessage
|
||||
if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
paymentReq, err := SerializePaymentRequest(reqData)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid payment request")
|
||||
return
|
||||
}
|
||||
|
||||
// Process payment
|
||||
tx, err := paymentHandler.ProcessPayment(paymentReq)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Payment processing failed")
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, tx)
|
||||
}
|
||||
|
||||
// handlePaymentValidate validates a payment method
|
||||
func handlePaymentValidate(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse validation request
|
||||
var req struct {
|
||||
Method string `json:"method"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
// Validate payment method
|
||||
valid, err := paymentHandler.ValidatePaymentMethod(req.Method, req.Data)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Validation failed")
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"valid": valid,
|
||||
"method": req.Method,
|
||||
"message": "Payment method validation complete",
|
||||
})
|
||||
}
|
||||
|
||||
// handlePaymentStatus returns payment transaction status
|
||||
func handlePaymentStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Extract transaction ID from path
|
||||
txID := strings.TrimPrefix(r.URL.Path, "/api/payment/status/")
|
||||
if txID == "" {
|
||||
writeError(w, http.StatusBadRequest, "Transaction ID required")
|
||||
return
|
||||
}
|
||||
|
||||
// Get transaction from handler
|
||||
tx, exists := paymentHandler.GetTransaction(txID)
|
||||
if !exists {
|
||||
writeError(w, http.StatusNotFound, "Transaction not found")
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, tx)
|
||||
}
|
||||
|
||||
// handlePaymentRefund processes a refund
|
||||
func handlePaymentRefund(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement refund processing
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"refund_id": "ref_" + generateID(),
|
||||
"status": "processing",
|
||||
"message": "Refund initiated",
|
||||
})
|
||||
}
|
||||
|
||||
// OIDC Handlers
|
||||
|
||||
// handleOIDCDiscovery returns OIDC discovery document
|
||||
func handleOIDCDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
discovery := oidcProvider.GetDiscovery()
|
||||
writeJSON(w, http.StatusOK, discovery)
|
||||
}
|
||||
|
||||
// handleJWKS returns JSON Web Key Set
|
||||
func handleJWKS(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
jwk := jwtManager.GetPublicKeyJWK()
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"keys": []map[string]interface{}{jwk},
|
||||
})
|
||||
}
|
||||
|
||||
// handleAuthorize handles authorization requests
|
||||
func handleAuthorize(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" && r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse authorization request
|
||||
clientID := r.FormValue("client_id")
|
||||
redirectURI := r.FormValue("redirect_uri")
|
||||
responseType := r.FormValue("response_type")
|
||||
scope := r.FormValue("scope")
|
||||
state := r.FormValue("state")
|
||||
nonce := r.FormValue("nonce")
|
||||
codeChallenge := r.FormValue("code_challenge")
|
||||
codeChallengeMethod := r.FormValue("code_challenge_method")
|
||||
|
||||
// Validate request
|
||||
if clientID == "" || redirectURI == "" || responseType == "" {
|
||||
writeError(w, http.StatusBadRequest, "Missing required parameters")
|
||||
return
|
||||
}
|
||||
|
||||
// For demo, auto-approve with test user
|
||||
userID := "test-user"
|
||||
|
||||
// Generate authorization code
|
||||
authCode, err := oidcProvider.GenerateAuthorizationCode(
|
||||
clientID, redirectURI, scope, state, nonce, userID,
|
||||
codeChallenge, codeChallengeMethod,
|
||||
)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Return authorization code
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"code": authCode.Code,
|
||||
"state": state,
|
||||
"redirect_uri": redirectURI,
|
||||
})
|
||||
}
|
||||
|
||||
// handleToken handles token requests
|
||||
func handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse token request
|
||||
var req TokenRequest
|
||||
req.GrantType = r.FormValue("grant_type")
|
||||
req.Code = r.FormValue("code")
|
||||
req.RedirectURI = r.FormValue("redirect_uri")
|
||||
req.ClientID = r.FormValue("client_id")
|
||||
req.ClientSecret = r.FormValue("client_secret")
|
||||
req.RefreshToken = r.FormValue("refresh_token")
|
||||
req.Scope = r.FormValue("scope")
|
||||
req.CodeVerifier = r.FormValue("code_verifier")
|
||||
|
||||
// Handle based on grant type
|
||||
var resp *TokenResponse
|
||||
var err error
|
||||
|
||||
switch req.GrantType {
|
||||
case "authorization_code":
|
||||
resp, err = oidcProvider.ExchangeCode(&req)
|
||||
case "refresh_token":
|
||||
// TODO: Implement refresh token flow
|
||||
writeError(w, http.StatusNotImplemented, "Refresh token not yet implemented")
|
||||
return
|
||||
default:
|
||||
writeError(w, http.StatusBadRequest, "Unsupported grant type")
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// handleUserInfo returns user information
|
||||
func handleUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
handleCORS(w)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" && r.Method != "POST" {
|
||||
writeError(w, http.StatusMethodNotAllowed, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// Get bearer token from Authorization header
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
writeError(w, http.StatusUnauthorized, "Missing authorization header")
|
||||
return
|
||||
}
|
||||
|
||||
// Extract token
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||
writeError(w, http.StatusUnauthorized, "Invalid authorization header")
|
||||
return
|
||||
}
|
||||
|
||||
accessToken := parts[1]
|
||||
|
||||
// Get user info
|
||||
userInfo, err := oidcProvider.GetUserInfo(accessToken)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusUnauthorized, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, userInfo)
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
// handleCORS handles CORS preflight requests
|
||||
func handleCORS(w http.ResponseWriter) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// writeJSON writes JSON response
|
||||
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.WriteHeader(status)
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
// writeError writes error response
|
||||
func writeError(w http.ResponseWriter, status int, message string) {
|
||||
writeJSON(w, status, map[string]string{"error": message})
|
||||
}
|
||||
|
||||
// generateID generates a simple ID
|
||||
func generateID() string {
|
||||
return time.Now().Format("20060102150405")
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestHealthEndpoint tests the health check endpoint
|
||||
func TestHealthEndpoint(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleHealth(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["status"] != "healthy" {
|
||||
t.Errorf("Expected status healthy, got %v", response["status"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestPaymentInstruments tests getting payment instruments
|
||||
func TestPaymentInstruments(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/payment/instruments", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handlePaymentInstruments(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var instruments []map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&instruments); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if len(instruments) == 0 {
|
||||
t.Error("Expected at least one payment instrument")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCanMakePayment tests payment capability check
|
||||
func TestCanMakePayment(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
"origin": "https://localhost:3000",
|
||||
"methodData": []map[string]interface{}{
|
||||
{
|
||||
"supportedMethods": "https://motor.sonr.io/pay",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
req := httptest.NewRequest("POST", "/payment/canmakepayment", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleCanMakePayment(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["canMakePayment"] != true {
|
||||
t.Errorf("Expected canMakePayment to be true")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcessPayment tests payment processing with security
|
||||
func TestProcessPayment(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"origin": "https://localhost:3000",
|
||||
"topOrigin": "https://localhost:3000",
|
||||
"paymentRequestId": "test-request-123",
|
||||
"methodData": []map[string]interface{}{
|
||||
{
|
||||
"supportedMethods": "https://motor.sonr.io/pay",
|
||||
},
|
||||
},
|
||||
"details": map[string]interface{}{
|
||||
"total": map[string]interface{}{
|
||||
"label": "Test Payment",
|
||||
"amount": map[string]interface{}{
|
||||
"currency": "USD",
|
||||
"value": "100.00",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
req := httptest.NewRequest("POST", "/api/payment/process", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Origin", "https://localhost:3000")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleProcessPayment(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if response["paymentId"] == "" {
|
||||
t.Error("Expected payment ID in response")
|
||||
}
|
||||
|
||||
if response["status"] != "pending" {
|
||||
t.Errorf("Expected status pending, got %v", response["status"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestCardTokenization tests PCI-compliant card tokenization
|
||||
func TestCardTokenization(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Test valid card
|
||||
token, err := TokenizeCard("4111111111111111", "123", 12, 2025)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to tokenize valid card: %v", err)
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
t.Error("Expected token to be generated")
|
||||
}
|
||||
|
||||
// Test invalid card number
|
||||
_, err = TokenizeCard("1234567890123456", "123", 12, 2025)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid card number")
|
||||
}
|
||||
|
||||
// Test expired card
|
||||
_, err = TokenizeCard("4111111111111111", "123", 1, 2020)
|
||||
if err == nil {
|
||||
t.Error("Expected error for expired card")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransactionSigning tests transaction signature verification
|
||||
func TestTransactionSigning(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
txData := map[string]interface{}{
|
||||
"id": "test-tx-123",
|
||||
"amount": "100.00",
|
||||
"currency": "USD",
|
||||
"method": "card",
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Sign transaction
|
||||
signature, err := SignTransaction(txData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to sign transaction: %v", err)
|
||||
}
|
||||
|
||||
if signature == "" {
|
||||
t.Error("Expected signature to be generated")
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
valid := VerifyTransactionSignature(txData, signature)
|
||||
if !valid {
|
||||
t.Error("Expected signature to be valid")
|
||||
}
|
||||
|
||||
// Test invalid signature
|
||||
invalid := VerifyTransactionSignature(txData, "invalid-signature")
|
||||
if invalid {
|
||||
t.Error("Expected invalid signature to fail verification")
|
||||
}
|
||||
}
|
||||
|
||||
// TestOIDCDiscovery tests OIDC discovery endpoint
|
||||
func TestOIDCDiscovery(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/.well-known/openid-configuration", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleOIDCDiscovery(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var config map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&config); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
// Check required OIDC fields
|
||||
requiredFields := []string{
|
||||
"issuer",
|
||||
"authorization_endpoint",
|
||||
"token_endpoint",
|
||||
"userinfo_endpoint",
|
||||
"jwks_uri",
|
||||
}
|
||||
|
||||
for _, field := range requiredFields {
|
||||
if _, exists := config[field]; !exists {
|
||||
t.Errorf("Missing required OIDC field: %s", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestJWKS tests JWKS endpoint
|
||||
func TestJWKS(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/.well-known/jwks.json", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleJWKS(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var jwks map[string]interface{}
|
||||
if err := json.NewDecoder(w.Body).Decode(&jwks); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
keys, ok := jwks["keys"].([]interface{})
|
||||
if !ok || len(keys) == 0 {
|
||||
t.Error("Expected at least one key in JWKS")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRateLimiting tests rate limiting functionality
|
||||
func TestRateLimiting(t *testing.T) {
|
||||
// Initialize with low rate limit for testing
|
||||
securityConfig.RateLimit = 5
|
||||
securityConfig.RateWindow = time.Second
|
||||
rateLimiter = NewRateLimiter(5, time.Second)
|
||||
|
||||
// Make requests up to the limit
|
||||
for i := 0; i < 5; i++ {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Request %d: Expected status 200, got %d", i+1, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Next request should be rate limited
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusTooManyRequests {
|
||||
t.Errorf("Expected rate limit (429), got %d", w.Code)
|
||||
}
|
||||
|
||||
// Wait for rate limit window to reset
|
||||
time.Sleep(time.Second + 100*time.Millisecond)
|
||||
|
||||
// Should work again
|
||||
req = httptest.NewRequest("GET", "/health", nil)
|
||||
req.Header.Set("Origin", "test-client")
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("After reset: Expected status 200, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSecurityHeaders tests security headers are properly set
|
||||
func TestSecurityHeaders(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
SecurityMiddleware(handleHealth)(w, req)
|
||||
|
||||
// Check security headers
|
||||
headers := map[string]string{
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "DENY",
|
||||
"X-XSS-Protection": "1; mode=block",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
}
|
||||
|
||||
for header, expected := range headers {
|
||||
actual := w.Header().Get(header)
|
||||
if actual != expected {
|
||||
t.Errorf("Header %s: expected %s, got %s", header, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Check CSP is present
|
||||
csp := w.Header().Get("Content-Security-Policy")
|
||||
if csp == "" {
|
||||
t.Error("Expected Content-Security-Policy header")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPCICompliance tests PCI compliance audit logging
|
||||
func TestPCICompliance(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Log some actions
|
||||
pciCompliance.LogAction("TEST_ACTION", "user123", "resource456", "SUCCESS", "127.0.0.1")
|
||||
|
||||
// Get audit log
|
||||
logs := pciCompliance.GetAuditLog(10)
|
||||
|
||||
if len(logs) == 0 {
|
||||
t.Error("Expected audit log entries")
|
||||
}
|
||||
|
||||
// Check last entry
|
||||
lastLog := logs[len(logs)-1]
|
||||
if lastLog.Action != "TEST_ACTION" {
|
||||
t.Errorf("Expected action TEST_ACTION, got %s", lastLog.Action)
|
||||
}
|
||||
|
||||
if lastLog.UserID != "user123" {
|
||||
t.Errorf("Expected user ID user123, got %s", lastLog.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDataEncryption tests sensitive data encryption
|
||||
func TestDataEncryption(t *testing.T) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
sensitiveData := "4111-1111-1111-1111"
|
||||
|
||||
// Encrypt data
|
||||
encrypted, err := EncryptSensitiveData(sensitiveData)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encrypt data: %v", err)
|
||||
}
|
||||
|
||||
if encrypted == sensitiveData {
|
||||
t.Error("Encrypted data should not match plaintext")
|
||||
}
|
||||
|
||||
// Decrypt data
|
||||
decrypted, err := DecryptSensitiveData(encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decrypt data: %v", err)
|
||||
}
|
||||
|
||||
if decrypted != sensitiveData {
|
||||
t.Errorf("Decrypted data doesn't match original: got %s, want %s", decrypted, sensitiveData)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCardMasking tests card number masking
|
||||
func TestCardMasking(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"4111111111111111", "**** **** **** 1111"},
|
||||
{"5500000000000004", "**** **** **** 0004"},
|
||||
{"340000000000009", "********** 00009"},
|
||||
{"123", "123"}, // Too short to mask
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
masked := MaskCardNumber(tc.input)
|
||||
if masked != tc.expected {
|
||||
t.Errorf("MaskCardNumber(%s): got %s, want %s", tc.input, masked, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
-273
@@ -1,273 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// JWTManager handles JWT token operations
|
||||
type JWTManager struct {
|
||||
privateKey *rsa.PrivateKey
|
||||
publicKey *rsa.PublicKey
|
||||
kid string
|
||||
issuer string
|
||||
}
|
||||
|
||||
// JWTHeader represents JWT header
|
||||
type JWTHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Typ string `json:"typ"`
|
||||
Kid string `json:"kid,omitempty"`
|
||||
}
|
||||
|
||||
// JWTClaims represents standard JWT claims
|
||||
type JWTClaims struct {
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
Subject string `json:"sub,omitempty"`
|
||||
Audience interface{} `json:"aud,omitempty"` // Can be string or []string
|
||||
Expiration int64 `json:"exp,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
JWTID string `json:"jti,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
Extra map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// IDToken represents an OpenID Connect ID token
|
||||
type IDToken struct {
|
||||
JWTClaims
|
||||
AuthTime int64 `json:"auth_time,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
ACR string `json:"acr,omitempty"`
|
||||
AMR []string `json:"amr,omitempty"`
|
||||
AZP string `json:"azp,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
GivenName string `json:"given_name,omitempty"`
|
||||
FamilyName string `json:"family_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
EmailVerified bool `json:"email_verified,omitempty"`
|
||||
}
|
||||
|
||||
// Global JWT manager instance
|
||||
var jwtManager *JWTManager
|
||||
|
||||
// InitJWTManager initializes the JWT manager
|
||||
func InitJWTManager() error {
|
||||
// Generate RSA key pair
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate RSA key: %w", err)
|
||||
}
|
||||
|
||||
jwtManager = &JWTManager{
|
||||
privateKey: privateKey,
|
||||
publicKey: &privateKey.PublicKey,
|
||||
kid: "motor-key-1",
|
||||
issuer: "https://motor.sonr.io",
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateToken generates a JWT token
|
||||
func (m *JWTManager) GenerateToken(claims JWTClaims) (string, error) {
|
||||
// Set standard claims
|
||||
if claims.Issuer == "" {
|
||||
claims.Issuer = m.issuer
|
||||
}
|
||||
if claims.IssuedAt == 0 {
|
||||
claims.IssuedAt = time.Now().Unix()
|
||||
}
|
||||
if claims.Expiration == 0 {
|
||||
claims.Expiration = time.Now().Add(1 * time.Hour).Unix()
|
||||
}
|
||||
|
||||
// Create header
|
||||
header := JWTHeader{
|
||||
Alg: "RS256",
|
||||
Typ: "JWT",
|
||||
Kid: m.kid,
|
||||
}
|
||||
|
||||
// Encode header
|
||||
headerJSON, _ := json.Marshal(header)
|
||||
headerEncoded := base64.RawURLEncoding.EncodeToString(headerJSON)
|
||||
|
||||
// Encode claims
|
||||
claimsJSON, _ := json.Marshal(claims)
|
||||
claimsEncoded := base64.RawURLEncoding.EncodeToString(claimsJSON)
|
||||
|
||||
// Create signature
|
||||
message := headerEncoded + "." + claimsEncoded
|
||||
hash := sha256.Sum256([]byte(message))
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, m.privateKey, crypto.SHA256, hash[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
signatureEncoded := base64.RawURLEncoding.EncodeToString(signature)
|
||||
|
||||
// Combine parts
|
||||
token := message + "." + signatureEncoded
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GenerateIDToken generates an OpenID Connect ID token
|
||||
func (m *JWTManager) GenerateIDToken(subject, audience, nonce string, extra map[string]interface{}) (string, error) {
|
||||
idToken := IDToken{
|
||||
JWTClaims: JWTClaims{
|
||||
Issuer: m.issuer,
|
||||
Subject: subject,
|
||||
Audience: audience,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
Expiration: time.Now().Add(1 * time.Hour).Unix(),
|
||||
Nonce: nonce,
|
||||
},
|
||||
AuthTime: time.Now().Unix(),
|
||||
Email: fmt.Sprintf("%s@motor.sonr.io", subject),
|
||||
EmailVerified: true,
|
||||
}
|
||||
|
||||
// Convert to claims
|
||||
claims := JWTClaims{
|
||||
Issuer: idToken.Issuer,
|
||||
Subject: idToken.Subject,
|
||||
Audience: idToken.Audience,
|
||||
IssuedAt: idToken.IssuedAt,
|
||||
Expiration: idToken.Expiration,
|
||||
Nonce: idToken.Nonce,
|
||||
Extra: map[string]interface{}{
|
||||
"auth_time": idToken.AuthTime,
|
||||
"email": idToken.Email,
|
||||
"email_verified": idToken.EmailVerified,
|
||||
},
|
||||
}
|
||||
|
||||
// Add extra claims
|
||||
for k, v := range extra {
|
||||
claims.Extra[k] = v
|
||||
}
|
||||
|
||||
return m.GenerateToken(claims)
|
||||
}
|
||||
|
||||
// ValidateToken validates a JWT token
|
||||
func (m *JWTManager) ValidateToken(tokenString string) (*JWTClaims, error) {
|
||||
// Split token
|
||||
parts := strings.Split(tokenString, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("invalid token format")
|
||||
}
|
||||
|
||||
// Decode header
|
||||
headerJSON, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode header: %w", err)
|
||||
}
|
||||
|
||||
var header JWTHeader
|
||||
if err := json.Unmarshal(headerJSON, &header); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse header: %w", err)
|
||||
}
|
||||
|
||||
// Verify algorithm
|
||||
if header.Alg != "RS256" {
|
||||
return nil, fmt.Errorf("unsupported algorithm: %s", header.Alg)
|
||||
}
|
||||
|
||||
// Decode claims
|
||||
claimsJSON, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode claims: %w", err)
|
||||
}
|
||||
|
||||
var claims JWTClaims
|
||||
if err := json.Unmarshal(claimsJSON, &claims); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse claims: %w", err)
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
message := parts[0] + "." + parts[1]
|
||||
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode signature: %w", err)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256([]byte(message))
|
||||
if err := rsa.VerifyPKCS1v15(m.publicKey, crypto.SHA256, hash[:], signature); err != nil {
|
||||
return nil, fmt.Errorf("invalid signature: %w", err)
|
||||
}
|
||||
|
||||
// Verify expiration
|
||||
if claims.Expiration > 0 && time.Now().Unix() > claims.Expiration {
|
||||
return nil, fmt.Errorf("token expired")
|
||||
}
|
||||
|
||||
// Verify not before
|
||||
if claims.NotBefore > 0 && time.Now().Unix() < claims.NotBefore {
|
||||
return nil, fmt.Errorf("token not yet valid")
|
||||
}
|
||||
|
||||
return &claims, nil
|
||||
}
|
||||
|
||||
// GetPublicKeyJWK returns the public key in JWK format
|
||||
func (m *JWTManager) GetPublicKeyJWK() map[string]interface{} {
|
||||
// Get modulus and exponent
|
||||
n := base64.RawURLEncoding.EncodeToString(m.publicKey.N.Bytes())
|
||||
e := base64.RawURLEncoding.EncodeToString([]byte{1, 0, 1}) // 65537
|
||||
|
||||
return map[string]interface{}{
|
||||
"kty": "RSA",
|
||||
"use": "sig",
|
||||
"kid": m.kid,
|
||||
"alg": "RS256",
|
||||
"n": n,
|
||||
"e": e,
|
||||
}
|
||||
}
|
||||
|
||||
// GetPublicKeyPEM returns the public key in PEM format
|
||||
func (m *JWTManager) GetPublicKeyPEM() string {
|
||||
pubKeyBytes, _ := x509.MarshalPKIXPublicKey(m.publicKey)
|
||||
pubKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: pubKeyBytes,
|
||||
})
|
||||
return string(pubKeyPEM)
|
||||
}
|
||||
|
||||
// GenerateAccessToken generates an access token
|
||||
func (m *JWTManager) GenerateAccessToken(subject, scope string) (string, error) {
|
||||
claims := JWTClaims{
|
||||
Subject: subject,
|
||||
Extra: map[string]interface{}{
|
||||
"scope": scope,
|
||||
"token_type": "Bearer",
|
||||
},
|
||||
}
|
||||
return m.GenerateToken(claims)
|
||||
}
|
||||
|
||||
// GenerateRefreshToken generates a refresh token
|
||||
func (m *JWTManager) GenerateRefreshToken(subject string) (string, error) {
|
||||
claims := JWTClaims{
|
||||
Subject: subject,
|
||||
Expiration: time.Now().Add(30 * 24 * time.Hour).Unix(), // 30 days
|
||||
Extra: map[string]interface{}{
|
||||
"token_type": "refresh",
|
||||
},
|
||||
}
|
||||
return m.GenerateToken(claims)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
wasmhttp "github.com/go-sonr/wasm-http-server/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set up HTTP routes
|
||||
setupRoutes()
|
||||
|
||||
// Start the WASM HTTP server
|
||||
log.Println("Motor Payment Gateway & OIDC Server starting...")
|
||||
log.Println("Available endpoints:")
|
||||
log.Println(" Health: /health, /status")
|
||||
log.Println(" Payment API: /api/payment/*")
|
||||
log.Println(" OIDC: /.well-known/*, /authorize, /token, /userinfo")
|
||||
|
||||
wasmhttp.Serve(nil)
|
||||
}
|
||||
|
||||
// setupRoutes configures all HTTP routes with security middleware
|
||||
func setupRoutes() {
|
||||
// Health and status endpoints (no rate limiting)
|
||||
http.HandleFunc("/health", handleHealth)
|
||||
http.HandleFunc("/status", handleStatus)
|
||||
|
||||
// W3C Payment Handler API endpoints with security
|
||||
http.HandleFunc("/payment/instruments", SecurityMiddleware(handlePaymentInstruments))
|
||||
http.HandleFunc("/payment/canmakepayment", SecurityMiddleware(handleCanMakePayment))
|
||||
http.HandleFunc("/payment/paymentrequest", SecurityMiddleware(handlePaymentRequest))
|
||||
|
||||
// Payment Gateway endpoints with security
|
||||
http.HandleFunc("/api/payment/process", SecurityMiddleware(handlePaymentProcess))
|
||||
http.HandleFunc("/api/payment/validate", SecurityMiddleware(handlePaymentValidate))
|
||||
http.HandleFunc("/api/payment/status/", SecurityMiddleware(handlePaymentStatus))
|
||||
http.HandleFunc("/api/payment/refund", SecurityMiddleware(handlePaymentRefund))
|
||||
|
||||
// OIDC endpoints with security
|
||||
http.HandleFunc("/.well-known/openid-configuration", handleOIDCDiscovery) // No rate limit for discovery
|
||||
http.HandleFunc("/.well-known/jwks.json", handleJWKS) // No rate limit for JWKS
|
||||
http.HandleFunc("/authorize", SecurityMiddleware(handleAuthorize))
|
||||
http.HandleFunc("/token", SecurityMiddleware(handleToken))
|
||||
http.HandleFunc("/userinfo", SecurityMiddleware(handleUserInfo))
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OIDCProvider manages OpenID Connect operations
|
||||
type OIDCProvider struct {
|
||||
mu sync.RWMutex
|
||||
issuer string
|
||||
authCodes map[string]*AuthorizationCode
|
||||
accessTokens map[string]*AccessToken
|
||||
refreshTokens map[string]*RefreshToken
|
||||
clients map[string]*OIDCClient
|
||||
users map[string]*User
|
||||
}
|
||||
|
||||
// AuthorizationCode represents an authorization code
|
||||
type AuthorizationCode struct {
|
||||
Code string
|
||||
ClientID string
|
||||
RedirectURI string
|
||||
Scope string
|
||||
State string
|
||||
Nonce string
|
||||
UserID string
|
||||
ExpiresAt time.Time
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
}
|
||||
|
||||
// AccessToken represents an access token
|
||||
type AccessToken struct {
|
||||
Token string
|
||||
ClientID string
|
||||
UserID string
|
||||
Scope string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// RefreshToken represents a refresh token
|
||||
type RefreshToken struct {
|
||||
Token string
|
||||
ClientID string
|
||||
UserID string
|
||||
Scope string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// OIDCClient represents an OIDC client application
|
||||
type OIDCClient struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
RedirectURIs []string
|
||||
GrantTypes []string
|
||||
ResponseTypes []string
|
||||
Scopes []string
|
||||
Name string
|
||||
}
|
||||
|
||||
// User represents a user
|
||||
type User struct {
|
||||
ID string
|
||||
Username string
|
||||
Email string
|
||||
EmailVerified bool
|
||||
Name string
|
||||
GivenName string
|
||||
FamilyName string
|
||||
}
|
||||
|
||||
// OIDCDiscovery represents OIDC discovery document
|
||||
type OIDCDiscovery struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
UserInfoEndpoint string `json:"userinfo_endpoint"`
|
||||
JWKSUri string `json:"jwks_uri"`
|
||||
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
||||
ScopesSupported []string `json:"scopes_supported"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported"`
|
||||
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
|
||||
GrantTypesSupported []string `json:"grant_types_supported"`
|
||||
ACRValuesSupported []string `json:"acr_values_supported,omitempty"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported"`
|
||||
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
||||
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
|
||||
ClaimsSupported []string `json:"claims_supported"`
|
||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
|
||||
}
|
||||
|
||||
// TokenRequest represents a token request
|
||||
type TokenRequest struct {
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code,omitempty"`
|
||||
RedirectURI string `json:"redirect_uri,omitempty"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
CodeVerifier string `json:"code_verifier,omitempty"`
|
||||
}
|
||||
|
||||
// TokenResponse represents a token response
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
// Global OIDC provider instance
|
||||
var oidcProvider = &OIDCProvider{
|
||||
issuer: "https://motor.sonr.io",
|
||||
authCodes: make(map[string]*AuthorizationCode),
|
||||
accessTokens: make(map[string]*AccessToken),
|
||||
refreshTokens: make(map[string]*RefreshToken),
|
||||
clients: make(map[string]*OIDCClient),
|
||||
users: make(map[string]*User),
|
||||
}
|
||||
|
||||
// Initialize OIDC provider
|
||||
func init() {
|
||||
// Initialize JWT manager
|
||||
InitJWTManager()
|
||||
|
||||
// Add default client for testing
|
||||
oidcProvider.clients["motor-client"] = &OIDCClient{
|
||||
ClientID: "motor-client",
|
||||
ClientSecret: "motor-secret",
|
||||
RedirectURIs: []string{"https://localhost:3000/callback", "http://localhost:3000/callback"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code", "token", "id_token"},
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
Name: "Motor Test Client",
|
||||
}
|
||||
|
||||
// Add default user for testing
|
||||
oidcProvider.users["test-user"] = &User{
|
||||
ID: "test-user",
|
||||
Username: "testuser",
|
||||
Email: "test@motor.sonr.io",
|
||||
EmailVerified: true,
|
||||
Name: "Test User",
|
||||
GivenName: "Test",
|
||||
FamilyName: "User",
|
||||
}
|
||||
}
|
||||
|
||||
// GetDiscovery returns OIDC discovery document
|
||||
func (p *OIDCProvider) GetDiscovery() *OIDCDiscovery {
|
||||
return &OIDCDiscovery{
|
||||
Issuer: p.issuer,
|
||||
AuthorizationEndpoint: "/authorize",
|
||||
TokenEndpoint: "/token",
|
||||
UserInfoEndpoint: "/userinfo",
|
||||
JWKSUri: "/.well-known/jwks.json",
|
||||
ScopesSupported: []string{
|
||||
"openid", "profile", "email", "offline_access",
|
||||
},
|
||||
ResponseTypesSupported: []string{
|
||||
"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token",
|
||||
},
|
||||
GrantTypesSupported: []string{
|
||||
"authorization_code", "implicit", "refresh_token",
|
||||
},
|
||||
SubjectTypesSupported: []string{"public"},
|
||||
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
||||
TokenEndpointAuthMethodsSupported: []string{
|
||||
"client_secret_basic", "client_secret_post",
|
||||
},
|
||||
ClaimsSupported: []string{
|
||||
"sub", "name", "given_name", "family_name", "email", "email_verified",
|
||||
},
|
||||
CodeChallengeMethodsSupported: []string{"plain", "S256"},
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateAuthorizationCode generates an authorization code
|
||||
func (p *OIDCProvider) GenerateAuthorizationCode(clientID, redirectURI, scope, state, nonce, userID string, codeChallenge, codeChallengeMethod string) (*AuthorizationCode, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Validate client
|
||||
client, exists := p.clients[clientID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("invalid client_id")
|
||||
}
|
||||
|
||||
// Validate redirect URI
|
||||
validRedirect := false
|
||||
for _, uri := range client.RedirectURIs {
|
||||
if uri == redirectURI {
|
||||
validRedirect = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validRedirect {
|
||||
return nil, fmt.Errorf("invalid redirect_uri")
|
||||
}
|
||||
|
||||
// Generate code
|
||||
code := generateRandomString(32)
|
||||
|
||||
authCode := &AuthorizationCode{
|
||||
Code: code,
|
||||
ClientID: clientID,
|
||||
RedirectURI: redirectURI,
|
||||
Scope: scope,
|
||||
State: state,
|
||||
Nonce: nonce,
|
||||
UserID: userID,
|
||||
ExpiresAt: time.Now().Add(10 * time.Minute),
|
||||
CodeChallenge: codeChallenge,
|
||||
CodeChallengeMethod: codeChallengeMethod,
|
||||
}
|
||||
|
||||
p.authCodes[code] = authCode
|
||||
|
||||
return authCode, nil
|
||||
}
|
||||
|
||||
// ExchangeCode exchanges authorization code for tokens
|
||||
func (p *OIDCProvider) ExchangeCode(req *TokenRequest) (*TokenResponse, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Get authorization code
|
||||
authCode, exists := p.authCodes[req.Code]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("invalid authorization code")
|
||||
}
|
||||
|
||||
// Validate code hasn't expired
|
||||
if time.Now().After(authCode.ExpiresAt) {
|
||||
delete(p.authCodes, req.Code)
|
||||
return nil, fmt.Errorf("authorization code expired")
|
||||
}
|
||||
|
||||
// Validate client
|
||||
if authCode.ClientID != req.ClientID {
|
||||
return nil, fmt.Errorf("client_id mismatch")
|
||||
}
|
||||
|
||||
// Validate redirect URI
|
||||
if authCode.RedirectURI != req.RedirectURI {
|
||||
return nil, fmt.Errorf("redirect_uri mismatch")
|
||||
}
|
||||
|
||||
// Validate PKCE if present
|
||||
if authCode.CodeChallenge != "" {
|
||||
if !validatePKCE(authCode.CodeChallenge, authCode.CodeChallengeMethod, req.CodeVerifier) {
|
||||
return nil, fmt.Errorf("invalid code_verifier")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete used code
|
||||
delete(p.authCodes, req.Code)
|
||||
|
||||
// Generate tokens
|
||||
accessToken, _ := jwtManager.GenerateAccessToken(authCode.UserID, authCode.Scope)
|
||||
refreshToken, _ := jwtManager.GenerateRefreshToken(authCode.UserID)
|
||||
idToken, _ := jwtManager.GenerateIDToken(authCode.UserID, authCode.ClientID, authCode.Nonce, nil)
|
||||
|
||||
// Store tokens
|
||||
p.accessTokens[accessToken] = &AccessToken{
|
||||
Token: accessToken,
|
||||
ClientID: authCode.ClientID,
|
||||
UserID: authCode.UserID,
|
||||
Scope: authCode.Scope,
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour),
|
||||
}
|
||||
|
||||
p.refreshTokens[refreshToken] = &RefreshToken{
|
||||
Token: refreshToken,
|
||||
ClientID: authCode.ClientID,
|
||||
UserID: authCode.UserID,
|
||||
Scope: authCode.Scope,
|
||||
ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
|
||||
}
|
||||
|
||||
return &TokenResponse{
|
||||
AccessToken: accessToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: 3600,
|
||||
RefreshToken: refreshToken,
|
||||
IDToken: idToken,
|
||||
Scope: authCode.Scope,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetUserInfo returns user information
|
||||
func (p *OIDCProvider) GetUserInfo(accessToken string) (map[string]interface{}, error) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
// Validate access token
|
||||
token, exists := p.accessTokens[accessToken]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("invalid access token")
|
||||
}
|
||||
|
||||
// Check expiration
|
||||
if time.Now().After(token.ExpiresAt) {
|
||||
return nil, fmt.Errorf("access token expired")
|
||||
}
|
||||
|
||||
// Get user
|
||||
user, exists := p.users[token.UserID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
// Return user info based on scope
|
||||
userInfo := map[string]interface{}{
|
||||
"sub": user.ID,
|
||||
}
|
||||
|
||||
// Add claims based on scope
|
||||
scopes := strings.Split(token.Scope, " ")
|
||||
for _, scope := range scopes {
|
||||
switch scope {
|
||||
case "profile":
|
||||
userInfo["name"] = user.Name
|
||||
userInfo["given_name"] = user.GivenName
|
||||
userInfo["family_name"] = user.FamilyName
|
||||
userInfo["preferred_username"] = user.Username
|
||||
case "email":
|
||||
userInfo["email"] = user.Email
|
||||
userInfo["email_verified"] = user.EmailVerified
|
||||
}
|
||||
}
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
// generateRandomString generates a random string
|
||||
func generateRandomString(length int) string {
|
||||
bytes := make([]byte, length)
|
||||
rand.Read(bytes)
|
||||
return base64.RawURLEncoding.EncodeToString(bytes)[:length]
|
||||
}
|
||||
|
||||
// validatePKCE validates PKCE code challenge
|
||||
func validatePKCE(codeChallenge, method, verifier string) bool {
|
||||
if method == "plain" {
|
||||
return codeChallenge == verifier
|
||||
}
|
||||
// For S256, would need to implement SHA256 hashing
|
||||
// For simplicity, returning true for now
|
||||
return true
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PaymentMethod represents a payment method according to W3C Payment Handler API
|
||||
type PaymentMethod struct {
|
||||
SupportedMethods string `json:"supportedMethods"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentDetails contains payment details
|
||||
type PaymentDetails struct {
|
||||
Total PaymentItem `json:"total"`
|
||||
DisplayItems []PaymentItem `json:"displayItems,omitempty"`
|
||||
Modifiers []interface{} `json:"modifiers,omitempty"`
|
||||
ShippingOptions []interface{} `json:"shippingOptions,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentItem represents an item in payment
|
||||
type PaymentItem struct {
|
||||
Label string `json:"label"`
|
||||
Amount PaymentCurrency `json:"amount"`
|
||||
}
|
||||
|
||||
// PaymentCurrency represents currency amount
|
||||
type PaymentCurrency struct {
|
||||
Currency string `json:"currency"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// PaymentRequest represents a W3C Payment Request
|
||||
type PaymentRequest struct {
|
||||
ID string `json:"id"`
|
||||
MethodData []PaymentMethod `json:"methodData"`
|
||||
Details PaymentDetails `json:"details"`
|
||||
Options PaymentOptions `json:"options,omitempty"`
|
||||
Origin string `json:"origin"`
|
||||
TopOrigin string `json:"topOrigin"`
|
||||
PaymentRequestID string `json:"paymentRequestId"`
|
||||
Total PaymentItem `json:"total"`
|
||||
}
|
||||
|
||||
// PaymentOptions contains payment options
|
||||
type PaymentOptions struct {
|
||||
RequestPayerName bool `json:"requestPayerName,omitempty"`
|
||||
RequestPayerEmail bool `json:"requestPayerEmail,omitempty"`
|
||||
RequestPayerPhone bool `json:"requestPayerPhone,omitempty"`
|
||||
RequestShipping bool `json:"requestShipping,omitempty"`
|
||||
ShippingType string `json:"shippingType,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentResponse represents response to payment request
|
||||
type PaymentResponse struct {
|
||||
RequestID string `json:"requestId"`
|
||||
MethodName string `json:"methodName"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
PayerName string `json:"payerName,omitempty"`
|
||||
PayerEmail string `json:"payerEmail,omitempty"`
|
||||
PayerPhone string `json:"payerPhone,omitempty"`
|
||||
ShippingAddress interface{} `json:"shippingAddress,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentTransaction represents a payment transaction
|
||||
type PaymentTransaction struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Amount PaymentCurrency `json:"amount"`
|
||||
Method string `json:"method"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Request *PaymentRequest `json:"request,omitempty"`
|
||||
Response *PaymentResponse `json:"response,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentHandler manages payment processing
|
||||
type PaymentHandler struct {
|
||||
mu sync.RWMutex
|
||||
transactions map[string]*PaymentTransaction
|
||||
instruments []PaymentInstrument
|
||||
}
|
||||
|
||||
// PaymentInstrument represents a payment instrument
|
||||
type PaymentInstrument struct {
|
||||
Name string `json:"name"`
|
||||
Icons []Icon `json:"icons,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
}
|
||||
|
||||
// Icon represents a payment instrument icon
|
||||
type Icon struct {
|
||||
Src string `json:"src"`
|
||||
Sizes string `json:"sizes,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Global payment handler instance
|
||||
var paymentHandler = &PaymentHandler{
|
||||
transactions: make(map[string]*PaymentTransaction),
|
||||
instruments: []PaymentInstrument{
|
||||
{
|
||||
Name: "Motor Payment",
|
||||
Method: "https://motor.sonr.io/pay",
|
||||
Capabilities: []string{"basic-card", "tokenized-card"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ProcessPayment processes a payment request with enhanced security
|
||||
func (h *PaymentHandler) ProcessPayment(req *PaymentRequest) (*PaymentTransaction, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
// Initialize payment security if not already done
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Validate origin for security
|
||||
if !ValidateOrigin(req.Origin) {
|
||||
return nil, fmt.Errorf("invalid origin: %s", req.Origin)
|
||||
}
|
||||
|
||||
// Generate transaction ID
|
||||
txID := generateTransactionID()
|
||||
|
||||
// Create transaction data for signing
|
||||
txData := map[string]interface{}{
|
||||
"id": txID,
|
||||
"amount": req.Details.Total.Amount.Value,
|
||||
"currency": req.Details.Total.Amount.Currency,
|
||||
"method": req.MethodData[0].SupportedMethods,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
|
||||
// Sign transaction for integrity
|
||||
signature, err := SignTransaction(txData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign transaction: %v", err)
|
||||
}
|
||||
|
||||
// Create transaction
|
||||
tx := &PaymentTransaction{
|
||||
ID: txID,
|
||||
Status: "pending",
|
||||
Amount: req.Details.Total.Amount,
|
||||
Method: req.MethodData[0].SupportedMethods,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Request: req,
|
||||
Metadata: map[string]interface{}{
|
||||
"origin": req.Origin,
|
||||
"topOrigin": req.TopOrigin,
|
||||
"signature": signature,
|
||||
},
|
||||
}
|
||||
|
||||
// Log for PCI compliance
|
||||
pciCompliance.LogAction("PROCESS_PAYMENT", "", txID, "INITIATED", req.Origin)
|
||||
|
||||
// Store transaction
|
||||
h.transactions[txID] = tx
|
||||
|
||||
// Process payment asynchronously with security checks
|
||||
go h.processPaymentSecurely(txID)
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// ValidatePaymentMethod validates a payment method
|
||||
func (h *PaymentHandler) ValidatePaymentMethod(method string, data interface{}) (bool, error) {
|
||||
// Check if method is supported
|
||||
for _, instrument := range h.instruments {
|
||||
if instrument.Method == method {
|
||||
// Perform validation based on method type
|
||||
switch method {
|
||||
case "basic-card", "https://motor.sonr.io/pay":
|
||||
return h.validateCardData(data)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GetTransaction retrieves a transaction by ID
|
||||
func (h *PaymentHandler) GetTransaction(id string) (*PaymentTransaction, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
tx, exists := h.transactions[id]
|
||||
return tx, exists
|
||||
}
|
||||
|
||||
// UpdateTransactionStatus updates transaction status
|
||||
func (h *PaymentHandler) UpdateTransactionStatus(id, status string) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if tx, exists := h.transactions[id]; exists {
|
||||
tx.Status = status
|
||||
tx.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanMakePayment checks if payment can be made
|
||||
func (h *PaymentHandler) CanMakePayment(methods []PaymentMethod) bool {
|
||||
for _, method := range methods {
|
||||
for _, instrument := range h.instruments {
|
||||
if instrument.Method == method.SupportedMethods {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetInstruments returns available payment instruments
|
||||
func (h *PaymentHandler) GetInstruments() []PaymentInstrument {
|
||||
return h.instruments
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
// generateTransactionID generates a unique transaction ID
|
||||
func generateTransactionID() string {
|
||||
bytes := make([]byte, 16)
|
||||
rand.Read(bytes)
|
||||
return "txn_" + hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
// validateCardData validates and tokenizes card payment data
|
||||
func (h *PaymentHandler) validateCardData(data interface{}) (bool, error) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
if data == nil {
|
||||
return false, fmt.Errorf("no payment data provided")
|
||||
}
|
||||
|
||||
// Parse card data
|
||||
cardData, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
return false, fmt.Errorf("invalid payment data format")
|
||||
}
|
||||
|
||||
// Extract card details
|
||||
cardNumber, hasNumber := cardData["cardNumber"].(string)
|
||||
cvv, hasCVV := cardData["cvv"].(string)
|
||||
expiryMonth, hasMonth := cardData["expiryMonth"].(float64)
|
||||
expiryYear, hasYear := cardData["expiryYear"].(float64)
|
||||
|
||||
if !hasNumber || !hasCVV || !hasMonth || !hasYear {
|
||||
return false, fmt.Errorf("missing required card fields")
|
||||
}
|
||||
|
||||
// Tokenize the card for PCI compliance
|
||||
token, err := TokenizeCard(cardNumber, cvv, int(expiryMonth), int(expiryYear))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("card validation failed: %v", err)
|
||||
}
|
||||
|
||||
// Replace sensitive data with token
|
||||
cardData["token"] = token
|
||||
cardData["cardNumber"] = MaskCardNumber(cardNumber)
|
||||
delete(cardData, "cvv") // Never store CVV
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// processPaymentSecurely processes payment with enhanced security
|
||||
func (h *PaymentHandler) processPaymentSecurely(txID string) {
|
||||
// Initialize payment security
|
||||
InitializePaymentSecurity()
|
||||
|
||||
// Simulate processing delay
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Verify transaction exists
|
||||
h.mu.RLock()
|
||||
tx, exists := h.transactions[txID]
|
||||
h.mu.RUnlock()
|
||||
|
||||
if !exists {
|
||||
pciCompliance.LogAction("PROCESS_PAYMENT", "", txID, "FAILED", "Transaction not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify transaction signature
|
||||
txData := map[string]interface{}{
|
||||
"id": txID,
|
||||
"amount": tx.Amount.Value,
|
||||
"currency": tx.Amount.Currency,
|
||||
"method": tx.Method,
|
||||
"timestamp": tx.CreatedAt.Unix(),
|
||||
}
|
||||
|
||||
if signature, ok := tx.Metadata["signature"].(string); ok {
|
||||
if !VerifyTransactionSignature(txData, signature) {
|
||||
h.UpdateTransactionStatus(txID, "failed")
|
||||
pciCompliance.LogAction("PROCESS_PAYMENT", "", txID, "FAILED", "Invalid signature")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update status to completed
|
||||
h.UpdateTransactionStatus(txID, "completed")
|
||||
|
||||
// Create secure payment response
|
||||
h.mu.Lock()
|
||||
if tx, exists := h.transactions[txID]; exists {
|
||||
// Generate response token
|
||||
responseToken := generateSecureToken()
|
||||
|
||||
tx.Response = &PaymentResponse{
|
||||
RequestID: tx.Request.PaymentRequestID,
|
||||
MethodName: tx.Method,
|
||||
Details: map[string]interface{}{
|
||||
"transactionId": txID,
|
||||
"status": "success",
|
||||
"token": responseToken,
|
||||
"timestamp": time.Now().Unix(),
|
||||
},
|
||||
}
|
||||
|
||||
// Log successful payment
|
||||
pciCompliance.LogAction("PROCESS_PAYMENT", "", txID, "SUCCESS", tx.Request.Origin)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// SerializePaymentRequest serializes a payment request from JSON
|
||||
func SerializePaymentRequest(data []byte) (*PaymentRequest, error) {
|
||||
var req PaymentRequest
|
||||
err := json.Unmarshal(data, &req)
|
||||
return &req, err
|
||||
}
|
||||
|
||||
// SerializePaymentResponse serializes a payment response to JSON
|
||||
func SerializePaymentResponse(resp *PaymentResponse) ([]byte, error) {
|
||||
return json.Marshal(resp)
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PaymentTokenizer handles secure payment method tokenization
|
||||
type PaymentTokenizer struct {
|
||||
mu sync.RWMutex
|
||||
tokens map[string]*TokenData
|
||||
encryptKey []byte
|
||||
}
|
||||
|
||||
// TokenData stores tokenized payment data
|
||||
type TokenData struct {
|
||||
Token string `json:"token"`
|
||||
LastFour string `json:"last_four"`
|
||||
Brand string `json:"brand"`
|
||||
ExpiryMonth int `json:"expiry_month"`
|
||||
ExpiryYear int `json:"expiry_year"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UsedCount int `json:"used_count"`
|
||||
}
|
||||
|
||||
// TransactionSigner handles transaction signing and verification
|
||||
type TransactionSigner struct {
|
||||
signKey []byte
|
||||
}
|
||||
|
||||
// PCICompliance handles PCI DSS compliance requirements
|
||||
type PCICompliance struct {
|
||||
auditLog []AuditEntry
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// AuditEntry for PCI compliance logging
|
||||
type AuditEntry struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Action string `json:"action"`
|
||||
UserID string `json:"user_id"`
|
||||
ResourceID string `json:"resource_id"`
|
||||
Result string `json:"result"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
}
|
||||
|
||||
var (
|
||||
paymentTokenizer *PaymentTokenizer
|
||||
transactionSigner *TransactionSigner
|
||||
pciCompliance *PCICompliance
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// InitializePaymentSecurity initializes payment security components
|
||||
func InitializePaymentSecurity() {
|
||||
initOnce.Do(func() {
|
||||
// Generate encryption key (in production, use KMS)
|
||||
encKey := make([]byte, 32)
|
||||
io.ReadFull(rand.Reader, encKey)
|
||||
|
||||
// Generate signing key
|
||||
signKey := make([]byte, 32)
|
||||
io.ReadFull(rand.Reader, signKey)
|
||||
|
||||
paymentTokenizer = &PaymentTokenizer{
|
||||
tokens: make(map[string]*TokenData),
|
||||
encryptKey: encKey,
|
||||
}
|
||||
|
||||
transactionSigner = &TransactionSigner{
|
||||
signKey: signKey,
|
||||
}
|
||||
|
||||
pciCompliance = &PCICompliance{
|
||||
auditLog: make([]AuditEntry, 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TokenizeCard tokenizes credit card data (PCI DSS compliant)
|
||||
func TokenizeCard(cardNumber, cvv string, expiryMonth, expiryYear int) (string, error) {
|
||||
// Validate card number using Luhn algorithm
|
||||
if !validateLuhn(cardNumber) {
|
||||
return "", fmt.Errorf("invalid card number")
|
||||
}
|
||||
|
||||
// Validate CVV
|
||||
if !validateCVV(cvv) {
|
||||
return "", fmt.Errorf("invalid CVV")
|
||||
}
|
||||
|
||||
// Validate expiry
|
||||
if !validateExpiry(expiryMonth, expiryYear) {
|
||||
return "", fmt.Errorf("card expired or invalid expiry date")
|
||||
}
|
||||
|
||||
// Extract card info
|
||||
lastFour := cardNumber[len(cardNumber)-4:]
|
||||
brand := detectCardBrand(cardNumber)
|
||||
|
||||
// Generate secure token
|
||||
token := generateSecureToken()
|
||||
|
||||
// Store tokenized data (never store raw card data)
|
||||
tokenData := &TokenData{
|
||||
Token: token,
|
||||
LastFour: lastFour,
|
||||
Brand: brand,
|
||||
ExpiryMonth: expiryMonth,
|
||||
ExpiryYear: expiryYear,
|
||||
CreatedAt: time.Now(),
|
||||
UsedCount: 0,
|
||||
}
|
||||
|
||||
paymentTokenizer.mu.Lock()
|
||||
paymentTokenizer.tokens[token] = tokenData
|
||||
paymentTokenizer.mu.Unlock()
|
||||
|
||||
// Log tokenization for PCI compliance
|
||||
pciCompliance.LogAction("TOKENIZE_CARD", "", token, "SUCCESS", "")
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// validateLuhn validates credit card number using Luhn algorithm
|
||||
func validateLuhn(cardNumber string) bool {
|
||||
// Remove spaces and dashes
|
||||
cardNumber = strings.ReplaceAll(cardNumber, " ", "")
|
||||
cardNumber = strings.ReplaceAll(cardNumber, "-", "")
|
||||
|
||||
// Check if all digits
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(cardNumber) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Luhn algorithm
|
||||
sum := 0
|
||||
isEven := false
|
||||
|
||||
for i := len(cardNumber) - 1; i >= 0; i-- {
|
||||
digit := int(cardNumber[i] - '0')
|
||||
|
||||
if isEven {
|
||||
digit *= 2
|
||||
if digit > 9 {
|
||||
digit -= 9
|
||||
}
|
||||
}
|
||||
|
||||
sum += digit
|
||||
isEven = !isEven
|
||||
}
|
||||
|
||||
return sum%10 == 0
|
||||
}
|
||||
|
||||
// validateCVV validates CVV format
|
||||
func validateCVV(cvv string) bool {
|
||||
// CVV should be 3 or 4 digits
|
||||
return regexp.MustCompile(`^\d{3,4}$`).MatchString(cvv)
|
||||
}
|
||||
|
||||
// validateExpiry validates card expiry date
|
||||
func validateExpiry(month, year int) bool {
|
||||
now := time.Now()
|
||||
currentYear := now.Year()
|
||||
currentMonth := int(now.Month())
|
||||
|
||||
// Check valid month
|
||||
if month < 1 || month > 12 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if expired
|
||||
if year < currentYear || (year == currentYear && month < currentMonth) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check reasonable future date (max 20 years)
|
||||
if year > currentYear+20 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// detectCardBrand detects card brand from number
|
||||
func detectCardBrand(cardNumber string) string {
|
||||
// Remove spaces and dashes
|
||||
cardNumber = strings.ReplaceAll(cardNumber, " ", "")
|
||||
cardNumber = strings.ReplaceAll(cardNumber, "-", "")
|
||||
|
||||
// Visa
|
||||
if strings.HasPrefix(cardNumber, "4") {
|
||||
return "visa"
|
||||
}
|
||||
|
||||
// Mastercard
|
||||
if regexp.MustCompile(`^5[1-5]`).MatchString(cardNumber) ||
|
||||
regexp.MustCompile(`^2[2-7]`).MatchString(cardNumber) {
|
||||
return "mastercard"
|
||||
}
|
||||
|
||||
// American Express
|
||||
if strings.HasPrefix(cardNumber, "34") || strings.HasPrefix(cardNumber, "37") {
|
||||
return "amex"
|
||||
}
|
||||
|
||||
// Discover
|
||||
if strings.HasPrefix(cardNumber, "6011") || strings.HasPrefix(cardNumber, "65") {
|
||||
return "discover"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// generateSecureToken generates a cryptographically secure token
|
||||
func generateSecureToken() string {
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
return "tok_" + base64.URLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// SignTransaction signs a transaction for integrity
|
||||
func SignTransaction(transactionData map[string]interface{}) (string, error) {
|
||||
// Serialize transaction data
|
||||
data, err := json.Marshal(transactionData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create HMAC signature
|
||||
h := hmac.New(sha256.New, transactionSigner.signKey)
|
||||
h.Write(data)
|
||||
signature := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
// Log signing for audit
|
||||
txID := ""
|
||||
if id, ok := transactionData["id"].(string); ok {
|
||||
txID = id
|
||||
}
|
||||
pciCompliance.LogAction("SIGN_TRANSACTION", "", txID, "SUCCESS", "")
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// VerifyTransactionSignature verifies a transaction signature
|
||||
func VerifyTransactionSignature(transactionData map[string]interface{}, signature string) bool {
|
||||
// Serialize transaction data
|
||||
data, err := json.Marshal(transactionData)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Create HMAC signature
|
||||
h := hmac.New(sha256.New, transactionSigner.signKey)
|
||||
h.Write(data)
|
||||
expectedSignature := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
// Compare signatures
|
||||
return hmac.Equal([]byte(signature), []byte(expectedSignature))
|
||||
}
|
||||
|
||||
// EncryptSensitiveData encrypts sensitive payment data
|
||||
func EncryptSensitiveData(plaintext string) (string, error) {
|
||||
// Create cipher
|
||||
block, err := aes.NewCipher(paymentTokenizer.encryptKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Generate nonce
|
||||
nonce := make([]byte, 12)
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Encrypt
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ciphertext := aesgcm.Seal(nil, nonce, []byte(plaintext), nil)
|
||||
|
||||
// Combine nonce and ciphertext
|
||||
combined := append(nonce, ciphertext...)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(combined), nil
|
||||
}
|
||||
|
||||
// DecryptSensitiveData decrypts sensitive payment data
|
||||
func DecryptSensitiveData(encrypted string) (string, error) {
|
||||
// Decode from base64
|
||||
combined, err := base64.StdEncoding.DecodeString(encrypted)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Extract nonce and ciphertext
|
||||
if len(combined) < 12 {
|
||||
return "", fmt.Errorf("invalid encrypted data")
|
||||
}
|
||||
|
||||
nonce := combined[:12]
|
||||
ciphertext := combined[12:]
|
||||
|
||||
// Create cipher
|
||||
block, err := aes.NewCipher(paymentTokenizer.encryptKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
// LogAction logs an action for PCI compliance audit
|
||||
func (p *PCICompliance) LogAction(action, userID, resourceID, result, ipAddress string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
entry := AuditEntry{
|
||||
Timestamp: time.Now(),
|
||||
Action: action,
|
||||
UserID: userID,
|
||||
ResourceID: resourceID,
|
||||
Result: result,
|
||||
IPAddress: ipAddress,
|
||||
}
|
||||
|
||||
p.auditLog = append(p.auditLog, entry)
|
||||
|
||||
// In production, persist to secure audit log storage
|
||||
// For now, just keep in memory (limited to last 10000 entries)
|
||||
if len(p.auditLog) > 10000 {
|
||||
p.auditLog = p.auditLog[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuditLog returns recent audit log entries
|
||||
func (p *PCICompliance) GetAuditLog(limit int) []AuditEntry {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if limit > len(p.auditLog) {
|
||||
limit = len(p.auditLog)
|
||||
}
|
||||
|
||||
// Return most recent entries
|
||||
start := len(p.auditLog) - limit
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
|
||||
return p.auditLog[start:]
|
||||
}
|
||||
|
||||
// ValidateToken validates a payment token
|
||||
func ValidateToken(token string) (*TokenData, error) {
|
||||
paymentTokenizer.mu.RLock()
|
||||
defer paymentTokenizer.mu.RUnlock()
|
||||
|
||||
tokenData, exists := paymentTokenizer.tokens[token]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("invalid token")
|
||||
}
|
||||
|
||||
// Check if token is expired (tokens valid for 1 hour)
|
||||
if time.Since(tokenData.CreatedAt) > time.Hour {
|
||||
return nil, fmt.Errorf("token expired")
|
||||
}
|
||||
|
||||
// Increment usage count
|
||||
tokenData.UsedCount++
|
||||
|
||||
return tokenData, nil
|
||||
}
|
||||
|
||||
// MaskCardNumber masks all but last 4 digits of card number
|
||||
func MaskCardNumber(cardNumber string) string {
|
||||
// Remove spaces and dashes
|
||||
cardNumber = strings.ReplaceAll(cardNumber, " ", "")
|
||||
cardNumber = strings.ReplaceAll(cardNumber, "-", "")
|
||||
|
||||
if len(cardNumber) < 4 {
|
||||
return strings.Repeat("*", len(cardNumber))
|
||||
}
|
||||
|
||||
lastFour := cardNumber[len(cardNumber)-4:]
|
||||
masked := strings.Repeat("*", len(cardNumber)-4) + lastFour
|
||||
|
||||
// Format based on card type
|
||||
if len(masked) == 16 {
|
||||
// Format as XXXX XXXX XXXX 1234
|
||||
return masked[:4] + " " + masked[4:8] + " " + masked[8:12] + " " + masked[12:]
|
||||
}
|
||||
|
||||
return masked
|
||||
}
|
||||
|
||||
// SanitizePaymentData removes sensitive data from payment objects
|
||||
func SanitizePaymentData(data map[string]interface{}) map[string]interface{} {
|
||||
sanitized := make(map[string]interface{})
|
||||
|
||||
// List of sensitive fields to exclude
|
||||
sensitiveFields := []string{
|
||||
"card_number", "cvv", "cvc", "card_code",
|
||||
"account_number", "routing_number", "pin",
|
||||
}
|
||||
|
||||
for key, value := range data {
|
||||
// Check if field is sensitive
|
||||
isSensitive := false
|
||||
keyLower := strings.ToLower(key)
|
||||
for _, sensitive := range sensitiveFields {
|
||||
if strings.Contains(keyLower, sensitive) {
|
||||
isSensitive = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isSensitive {
|
||||
sanitized[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RateLimiter implements rate limiting
|
||||
type RateLimiter struct {
|
||||
mu sync.RWMutex
|
||||
requests map[string]*RequestCounter
|
||||
limit int
|
||||
window time.Duration
|
||||
}
|
||||
|
||||
// RequestCounter tracks requests
|
||||
type RequestCounter struct {
|
||||
Count int
|
||||
ResetTime time.Time
|
||||
}
|
||||
|
||||
// SecurityConfig holds security configuration
|
||||
type SecurityConfig struct {
|
||||
EnableRateLimit bool
|
||||
RateLimit int
|
||||
RateWindow time.Duration
|
||||
EnableCSP bool
|
||||
CSPPolicy string
|
||||
}
|
||||
|
||||
// Global security configuration
|
||||
var securityConfig = &SecurityConfig{
|
||||
EnableRateLimit: true,
|
||||
RateLimit: 100, // 100 requests
|
||||
RateWindow: time.Minute,
|
||||
EnableCSP: true,
|
||||
CSPPolicy: "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' https:; img-src 'self' data: https:; style-src 'self' 'unsafe-inline';",
|
||||
}
|
||||
|
||||
// Global rate limiter
|
||||
var rateLimiter = NewRateLimiter(securityConfig.RateLimit, securityConfig.RateWindow)
|
||||
|
||||
// NewRateLimiter creates a new rate limiter
|
||||
func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
|
||||
return &RateLimiter{
|
||||
requests: make(map[string]*RequestCounter),
|
||||
limit: limit,
|
||||
window: window,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow checks if request is allowed
|
||||
func (rl *RateLimiter) Allow(identifier string) bool {
|
||||
rl.mu.Lock()
|
||||
defer rl.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
counter, exists := rl.requests[identifier]
|
||||
|
||||
if !exists || now.After(counter.ResetTime) {
|
||||
// Create new counter or reset existing one
|
||||
rl.requests[identifier] = &RequestCounter{
|
||||
Count: 1,
|
||||
ResetTime: now.Add(rl.window),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if counter.Count >= rl.limit {
|
||||
return false
|
||||
}
|
||||
|
||||
counter.Count++
|
||||
return true
|
||||
}
|
||||
|
||||
// SecurityMiddleware wraps handlers with security features
|
||||
func SecurityMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Apply security headers
|
||||
applySecurityHeaders(w)
|
||||
|
||||
// Rate limiting
|
||||
if securityConfig.EnableRateLimit {
|
||||
// Use client IP or a default identifier for WASM environment
|
||||
identifier := getClientIdentifier(r)
|
||||
if !rateLimiter.Allow(identifier) {
|
||||
writeError(w, http.StatusTooManyRequests, "Rate limit exceeded")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Call the next handler
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// applySecurityHeaders applies security headers to response
|
||||
func applySecurityHeaders(w http.ResponseWriter) {
|
||||
// CORS headers (already handled by handleCORS, but adding for completeness)
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
// Security headers
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||
w.Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
|
||||
|
||||
// Content Security Policy
|
||||
if securityConfig.EnableCSP {
|
||||
w.Header().Set("Content-Security-Policy", securityConfig.CSPPolicy)
|
||||
}
|
||||
|
||||
// Strict Transport Security (for HTTPS)
|
||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
|
||||
}
|
||||
|
||||
// getClientIdentifier gets a client identifier for rate limiting
|
||||
func getClientIdentifier(r *http.Request) string {
|
||||
// In WASM environment, we can't rely on real IP
|
||||
// Use a combination of headers for identification
|
||||
|
||||
// Try X-Forwarded-For
|
||||
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||
return xff
|
||||
}
|
||||
|
||||
// Try X-Real-IP
|
||||
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
||||
return xri
|
||||
}
|
||||
|
||||
// Try Origin header (common in browser requests)
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
return origin
|
||||
}
|
||||
|
||||
// Try User-Agent as last resort
|
||||
if ua := r.Header.Get("User-Agent"); ua != "" {
|
||||
return ua
|
||||
}
|
||||
|
||||
// Default identifier
|
||||
return "default-client"
|
||||
}
|
||||
|
||||
// ValidatePaymentData validates payment data for security
|
||||
func ValidatePaymentData(data map[string]interface{}) error {
|
||||
// Check for required fields
|
||||
requiredFields := []string{"amount", "currency"}
|
||||
for _, field := range requiredFields {
|
||||
if _, exists := data[field]; !exists {
|
||||
return fmt.Errorf("missing required field: %s", field)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate amount
|
||||
if amount, ok := data["amount"].(float64); ok {
|
||||
if amount <= 0 || amount > 1000000 {
|
||||
return fmt.Errorf("invalid amount")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate currency
|
||||
if currency, ok := data["currency"].(string); ok {
|
||||
validCurrencies := []string{"USD", "EUR", "GBP", "JPY"}
|
||||
valid := false
|
||||
for _, vc := range validCurrencies {
|
||||
if currency == vc {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("unsupported currency")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SanitizeInput sanitizes user input
|
||||
func SanitizeInput(input string) string {
|
||||
// Remove any potentially dangerous characters
|
||||
// This is a basic implementation - in production, use a proper sanitization library
|
||||
sanitized := input
|
||||
|
||||
// Remove script tags
|
||||
sanitized = strings.ReplaceAll(sanitized, "<script>", "")
|
||||
sanitized = strings.ReplaceAll(sanitized, "</script>", "")
|
||||
|
||||
// Remove other potentially dangerous HTML
|
||||
sanitized = strings.ReplaceAll(sanitized, "<iframe>", "")
|
||||
sanitized = strings.ReplaceAll(sanitized, "</iframe>", "")
|
||||
|
||||
// Limit length
|
||||
if len(sanitized) > 1000 {
|
||||
sanitized = sanitized[:1000]
|
||||
}
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
// TokenizePaymentMethod creates a token for payment method
|
||||
func TokenizePaymentMethod(method map[string]interface{}) string {
|
||||
// Create a secure token representing the payment method
|
||||
// In production, this would use proper tokenization service
|
||||
|
||||
token := generateRandomString(32)
|
||||
|
||||
// Store token mapping (in production, use secure storage)
|
||||
// For now, just return the token
|
||||
return "pmtoken_" + token
|
||||
}
|
||||
|
||||
// ValidateOrigin validates request origin
|
||||
func ValidateOrigin(origin string) bool {
|
||||
// List of allowed origins
|
||||
allowedOrigins := []string{
|
||||
"https://motor.sonr.io",
|
||||
"https://localhost:3000",
|
||||
"http://localhost:3000",
|
||||
"https://sonr.io",
|
||||
}
|
||||
|
||||
for _, allowed := range allowedOrigins {
|
||||
if origin == allowed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package main
|
||||
|
||||
// Version is set by commitizen during release process
|
||||
var Version = "dev"
|
||||
@@ -1,18 +0,0 @@
|
||||
[tool.commitizen]
|
||||
name = "cz_customize"
|
||||
tag_format = "snrd/v$version"
|
||||
ignored_tag_formats = ["*/v${version}", "v${version}"]
|
||||
version_scheme = "semver"
|
||||
version_provider = "scm"
|
||||
update_changelog_on_bump = true
|
||||
changelog_file = "CHANGELOG.md"
|
||||
major_version_zero = true
|
||||
annotated_tag = true
|
||||
pre_bump_hooks = ["bash scripts/hook-bump-pre.sh"]
|
||||
post_bump_hooks = ["goreleaser release --clean -f cmd/snrd/.goreleaser.yml"]
|
||||
|
||||
[tool.commitizen.customize]
|
||||
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
|
||||
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
|
||||
default_bump = "PATCH"
|
||||
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(core\\)(!)?:"
|
||||
@@ -1,331 +0,0 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
|
||||
---
|
||||
version: 2
|
||||
dist: dist/snrd
|
||||
monorepo:
|
||||
tag_prefix: snrd/
|
||||
|
||||
project_name: snrd
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
|
||||
builds:
|
||||
# Darwin AMD64 Build
|
||||
- id: snrd-darwin-amd64
|
||||
main: ./cmd/snrd
|
||||
binary: snrd
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
- CGO_LDFLAGS=-lm
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Name=sonr
|
||||
- -X github.com/cosmos/cosmos-sdk/version.AppName=snrd
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Version={{.Version}}
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Commit={{.Commit}}
|
||||
- -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger"
|
||||
- -s -w
|
||||
tags:
|
||||
- netgo
|
||||
- ledger
|
||||
|
||||
# Darwin ARM64 Build
|
||||
- id: snrd-darwin-arm64
|
||||
main: ./cmd/snrd
|
||||
binary: snrd
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=oa64-clang
|
||||
- CXX=oa64-clang++
|
||||
- CGO_LDFLAGS=-lm
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- arm64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Name=sonr
|
||||
- -X github.com/cosmos/cosmos-sdk/version.AppName=snrd
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Version={{.Version}}
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Commit={{.Commit}}
|
||||
- -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger"
|
||||
- -s -w
|
||||
tags:
|
||||
- netgo
|
||||
- ledger
|
||||
|
||||
# Linux AMD64 Build
|
||||
- id: snrd-linux-amd64
|
||||
main: ./cmd/snrd
|
||||
binary: snrd
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-linux-gnu-gcc
|
||||
- CXX=x86_64-linux-gnu-g++
|
||||
- CGO_LDFLAGS=-lm
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
goamd64:
|
||||
- v1
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Name=sonr
|
||||
- -X github.com/cosmos/cosmos-sdk/version.AppName=snrd
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Version={{.Version}}
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Commit={{.Commit}}
|
||||
- -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger"
|
||||
- -s -w
|
||||
tags:
|
||||
- netgo
|
||||
- ledger
|
||||
|
||||
# Linux ARM64 Build
|
||||
- id: snrd-linux-arm64
|
||||
main: ./cmd/snrd
|
||||
binary: snrd
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
- CXX=aarch64-linux-gnu-g++
|
||||
- CGO_LDFLAGS=-lm
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Name=sonr
|
||||
- -X github.com/cosmos/cosmos-sdk/version.AppName=snrd
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Version={{.Version}}
|
||||
- -X github.com/cosmos/cosmos-sdk/version.Commit={{.Commit}}
|
||||
- -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger"
|
||||
- -s -w
|
||||
tags:
|
||||
- netgo
|
||||
- ledger
|
||||
|
||||
aur_sources:
|
||||
- name: snrd
|
||||
disable: true
|
||||
homepage: "https://sonr.io"
|
||||
description: "Sonr blockchain daemon - decentralized identity and data storage network"
|
||||
maintainers:
|
||||
- "Sonr <support@sonr.io>"
|
||||
license: "GPL-3.0"
|
||||
private_key: "{{ .Env.AUR_KEY }}"
|
||||
git_url: "ssh://[email protected]/snrd.git"
|
||||
skip_upload: auto
|
||||
provides:
|
||||
- snrd
|
||||
conflicts:
|
||||
- snrd-bin
|
||||
depends:
|
||||
- glibc
|
||||
makedepends:
|
||||
- go
|
||||
- git
|
||||
- make
|
||||
commit_msg_template: "Update to {{ .Tag }}"
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: "prad@sonr.io"
|
||||
prepare: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
go mod download
|
||||
build: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
export CGO_ENABLED=1
|
||||
export CGO_CPPFLAGS="${CPPFLAGS}"
|
||||
export CGO_CFLAGS="${CFLAGS}"
|
||||
export CGO_CXXFLAGS="${CXXFLAGS}"
|
||||
export CGO_LDFLAGS="${LDFLAGS}"
|
||||
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||
go build \
|
||||
-ldflags="-w -s -buildid='' -linkmode=external \
|
||||
-X github.com/cosmos/cosmos-sdk/version.Name=sonr \
|
||||
-X github.com/cosmos/cosmos-sdk/version.AppName=snrd \
|
||||
-X github.com/cosmos/cosmos-sdk/version.Version=${pkgver} \
|
||||
-X 'github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger'" \
|
||||
-tags "netgo,ledger" \
|
||||
-o snrd ./cmd/snrd
|
||||
chmod +x ./snrd
|
||||
package: |-
|
||||
cd "${pkgname}_${pkgver}"
|
||||
|
||||
# bin
|
||||
install -Dm755 "./snrd" "${pkgdir}/usr/bin/snrd"
|
||||
|
||||
# license
|
||||
if [ -f "./LICENSE" ]; then
|
||||
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/snrd/LICENSE"
|
||||
fi
|
||||
|
||||
# readme
|
||||
if [ -f "./README.md" ]; then
|
||||
install -Dm644 "./README.md" "${pkgdir}/usr/share/doc/snrd/README.md"
|
||||
fi
|
||||
|
||||
# config directory
|
||||
install -dm755 "${pkgdir}/etc/snrd"
|
||||
install -dm755 "${pkgdir}/var/lib/snrd"
|
||||
backup:
|
||||
- /etc/snrd/config.toml
|
||||
- /etc/snrd/app.toml
|
||||
|
||||
nix:
|
||||
- name: snrd
|
||||
ids:
|
||||
- snrd
|
||||
homepage: "https://sonr.io"
|
||||
description: "Sonr blockchain daemon - decentralized identity network"
|
||||
license: "gpl3"
|
||||
path: pkgs/snrd/default.nix
|
||||
commit_msg_template: "snrd: {{ .Tag }}"
|
||||
dependencies:
|
||||
- stdenv
|
||||
- glibc
|
||||
extra_install: |-
|
||||
wrapProgram $out/bin/snrd --prefix PATH : ${lib.makeBinPath [ glibc stdenv.cc.cc.lib ]}
|
||||
repository:
|
||||
owner: sonr-io
|
||||
name: nur
|
||||
branch: main
|
||||
token: "{{ .Env.GITHUB_TOKEN }}"
|
||||
|
||||
archives:
|
||||
- id: snrd
|
||||
ids:
|
||||
- snrd-linux-amd64
|
||||
- snrd-linux-arm64
|
||||
- snrd-darwin-amd64
|
||||
- snrd-darwin-arm64
|
||||
name_template: >-
|
||||
snrd_{{ .Os }}_{{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }}
|
||||
formats: ["tar.gz"]
|
||||
files:
|
||||
- src: README*
|
||||
wrap_in_directory: false
|
||||
|
||||
homebrew_casks:
|
||||
- name: snrd
|
||||
ids:
|
||||
- snrd
|
||||
homepage: "https://sonr.io"
|
||||
description: "Sonr blockchain daemon - decentralized identity and data storage network"
|
||||
commit_msg_template: "Brew cask update for {{ .ProjectName }} version {{ .Tag }}"
|
||||
directory: Casks
|
||||
repository:
|
||||
owner: sonr-io
|
||||
name: homebrew-tap
|
||||
branch: main
|
||||
token: "{{ .Env.GITHUB_TOKEN }}"
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: "prad@sonr.io"
|
||||
hooks:
|
||||
post:
|
||||
install: |
|
||||
if OS.mac?
|
||||
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/snrd"]
|
||||
end
|
||||
|
||||
nfpms:
|
||||
- id: snrd
|
||||
package_name: snrd
|
||||
ids:
|
||||
- snrd-linux-amd64
|
||||
- snrd-linux-arm64
|
||||
file_name_template: "snrd_{{ .Os }}_{{ .Arch }}{{ .ConventionalExtension }}"
|
||||
vendor: Sonr
|
||||
homepage: "https://sonr.io"
|
||||
maintainer: "Sonr <support@sonr.io>"
|
||||
description: "Sonr is a decentralized, permissionless, and censorship-resistant identity network."
|
||||
license: "GPL-3.0"
|
||||
formats:
|
||||
- rpm
|
||||
- deb
|
||||
- apk
|
||||
- archlinux
|
||||
contents:
|
||||
- src: README*
|
||||
dst: /usr/share/doc/snrd
|
||||
bindir: /usr/bin
|
||||
section: net
|
||||
priority: optional
|
||||
|
||||
blobs:
|
||||
- provider: s3
|
||||
endpoint: https://eb37925850388bca807b7fab964c12bb.r2.cloudflarestorage.com
|
||||
bucket: releases
|
||||
region: auto
|
||||
directory: "snrd/{{ .Tag }}"
|
||||
ids:
|
||||
- snrd
|
||||
|
||||
release:
|
||||
disable: false
|
||||
github:
|
||||
owner: sonr-io
|
||||
name: sonr
|
||||
name_template: "{{.ProjectName}}/{{ .Tag }}"
|
||||
draft: false
|
||||
replace_existing_draft: false # Don't replace drafts
|
||||
replace_existing_artifacts: false # Append, don't replace
|
||||
mode: append # Explicitly set to append mode
|
||||
|
||||
checksum:
|
||||
name_template: "snrd_checksums.txt"
|
||||
|
||||
npms:
|
||||
- name: "@sonr.io/snrd"
|
||||
ids:
|
||||
- snrd
|
||||
description: "Sonr blockchain daemon - decentralized identity and data storage network"
|
||||
homepage: "https://sonr.io"
|
||||
license: "GPL-3.0"
|
||||
author: "Sonr <support@sonr.io>"
|
||||
repository: "https://github.com/sonr-io/sonr"
|
||||
bugs: "https://github.com/sonr-io/sonr/issues"
|
||||
keywords:
|
||||
- blockchain
|
||||
- cosmos
|
||||
- did
|
||||
- identity
|
||||
- web3
|
||||
- sonr
|
||||
access: public
|
||||
format: tar.gz
|
||||
|
||||
snapshot:
|
||||
version_template: "{{ incpatch .Version }}-dev"
|
||||
|
||||
# Changelog configuration
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- "^chore:"
|
||||
@@ -1,5 +0,0 @@
|
||||
## snrd/v0.0.3 (2025-10-04)
|
||||
|
||||
## snrd/v0.0.2 (2025-10-04)
|
||||
|
||||
## snrd/v0.0.1 (2025-10-03)
|
||||
@@ -1,89 +0,0 @@
|
||||
# Use build argument for base image to allow using pre-cached base
|
||||
ARG BASE_IMAGE=golang:1.24.7-alpine3.22
|
||||
|
||||
FROM ${BASE_IMAGE} AS builder
|
||||
SHELL ["/bin/sh", "-ecuxo", "pipefail"]
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
build-base \
|
||||
git \
|
||||
linux-headers \
|
||||
bash \
|
||||
binutils-gold \
|
||||
wget
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# Copy entire source code (including crypto module)
|
||||
COPY . .
|
||||
|
||||
# Fix git ownership issue
|
||||
RUN git config --global --add safe.directory /code
|
||||
|
||||
# Download WasmVM library
|
||||
RUN --mount=type=cache,target=/tmp/wasmvm \
|
||||
set -eux; \
|
||||
export ARCH=$(uname -m); \
|
||||
WASM_VERSION=$(go list -m all | grep github.com/CosmWasm/wasmvm | head -1 || echo ""); \
|
||||
if [ ! -z "${WASM_VERSION}" ]; then \
|
||||
WASMVM_REPO=$(echo $WASM_VERSION | awk '{print $1}'); \
|
||||
WASMVM_VERS=$(echo $WASM_VERSION | awk '{print $2}'); \
|
||||
WASMVM_FILE="libwasmvm_muslc.${ARCH}.a"; \
|
||||
if [ ! -f "/tmp/wasmvm/${WASMVM_FILE}" ]; then \
|
||||
wget -O "/tmp/wasmvm/${WASMVM_FILE}" "https://${WASMVM_REPO}/releases/download/${WASMVM_VERS}/${WASMVM_FILE}"; \
|
||||
fi; \
|
||||
cp "/tmp/wasmvm/${WASMVM_FILE}" /lib/libwasmvm_muslc.a; \
|
||||
fi
|
||||
|
||||
# Download Go modules
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download
|
||||
|
||||
# Build binary with optimizations
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
set -eux; \
|
||||
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev"); \
|
||||
COMMIT=$(git log -1 --format='%H' 2>/dev/null || echo "unknown"); \
|
||||
LEDGER_ENABLED=false BUILD_TAGS=muslc LINK_STATICALLY=true \
|
||||
CGO_ENABLED=1 GOOS=linux \
|
||||
go build \
|
||||
-mod=readonly \
|
||||
-tags "netgo,ledger,muslc" \
|
||||
-ldflags "-X github.com/cosmos/cosmos-sdk/version.Name=sonr \
|
||||
-X github.com/cosmos/cosmos-sdk/version.AppName=snrd \
|
||||
-X github.com/cosmos/cosmos-sdk/version.Version=${VERSION} \
|
||||
-X github.com/cosmos/cosmos-sdk/version.Commit=${COMMIT} \
|
||||
-X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger,muslc \
|
||||
-w -s -linkmode=external -extldflags '-Wl,-z,muldefs -static'" \
|
||||
-buildvcs=false \
|
||||
-trimpath \
|
||||
-o /code/build/snrd \
|
||||
./cmd/snrd; \
|
||||
file /code/build/snrd; \
|
||||
echo "Ensuring binary is statically linked ..."; \
|
||||
(file /code/build/snrd | grep "statically linked")
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Final minimal runtime image
|
||||
FROM alpine:3.17
|
||||
|
||||
LABEL org.opencontainers.image.title="Sonr Daemon"
|
||||
LABEL org.opencontainers.image.source="https://github.com/sonr-io/sonr"
|
||||
|
||||
# Copy binary from build stage
|
||||
COPY --from=builder /code/build/snrd /usr/bin
|
||||
COPY --from=builder /lib/libwasmvm_muslc.a /lib/libwasmvm_muslc.a
|
||||
COPY --from=builder /code/scripts/test_node.sh /usr/bin/testnet
|
||||
RUN chmod +x /usr/bin/testnet
|
||||
|
||||
# Set up dependencies
|
||||
ENV PACKAGES="curl make bash jq sed"
|
||||
|
||||
# Install minimum necessary dependencies
|
||||
RUN apk add --no-cache $PACKAGES
|
||||
|
||||
WORKDIR /opt
|
||||
+2
-4
@@ -109,13 +109,11 @@ test:
|
||||
|
||||
release:
|
||||
@echo "Creating snrd release..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/snrd/.cz.toml --no-raise 6,21 bump --yes --increment PATCH
|
||||
@cd $(GIT_ROOT) && goreleaser release --clean
|
||||
|
||||
snapshot:
|
||||
@echo "Dry-Run Bumping snrd version..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/snrd/.cz.toml bump --yes --dry-run --no-verify --increment PATCH
|
||||
@echo "Creating snrd snapshots for all platforms..."
|
||||
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean -f cmd/snrd/.goreleaser.yml
|
||||
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean
|
||||
|
||||
# Docker build targets
|
||||
docker:
|
||||
|
||||
-441
@@ -1,441 +0,0 @@
|
||||
module snrd
|
||||
|
||||
go 1.24.7
|
||||
|
||||
// overrides
|
||||
replace (
|
||||
cosmossdk.io/core => cosmossdk.io/core v0.11.0
|
||||
cosmossdk.io/store => github.com/evmos/cosmos-sdk/store v0.0.0-20240718141609-414cbd051fbe
|
||||
github.com/CosmWasm/wasmd => github.com/rollchains/wasmd v0.50.0-evm
|
||||
|
||||
github.com/cosmos/cosmos-sdk => github.com/strangelove-ventures/cosmos-sdk v0.0.0-20250317212103-0767f8c5b1e5
|
||||
github.com/cosmos/evm => github.com/strangelove-ventures/cosmos-evm v0.2.0
|
||||
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc2
|
||||
github.com/sonr-io/sonr => ../../
|
||||
github.com/sonr-io/sonr/crypto => ../../crypto
|
||||
github.com/spf13/viper => github.com/spf13/viper v1.17.0 // v1.18+ breaks app overrides
|
||||
nhooyr.io/websocket => nhooyr.io/websocket v1.8.17
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
|
||||
// Fix btcec version for evmos compatibility
|
||||
github.com/btcsuite/btcd/btcec/v2 => github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||
// dgrijalva/jwt-go is deprecated and doesn't receive security updates.
|
||||
// See: https://github.com/cosmos/cosmos-sdk/issues/13134
|
||||
github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
||||
// See: https://github.com/cosmos/cosmos-sdk/issues/10409
|
||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.8.1
|
||||
|
||||
// pin version! 126854af5e6d has issues with the store so that queries fail
|
||||
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
github.com/tyler-smith/go-bip39 => github.com/go-sonr/go-bip39 v1.1.1
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/log v1.5.0
|
||||
cosmossdk.io/tools/confix v0.1.2
|
||||
github.com/cometbft/cometbft v0.38.17
|
||||
github.com/cosmos/cosmos-db v1.1.1
|
||||
github.com/cosmos/cosmos-sdk v0.53.4
|
||||
github.com/cosmos/evm v0.1.0
|
||||
github.com/sonr-io/sonr v0.0.0-00010101000000-000000000000
|
||||
github.com/spf13/cast v1.9.2
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.6 // indirect
|
||||
cosmossdk.io/client/v2 v2.0.0-beta.7 // indirect
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/core v0.12.0 // indirect
|
||||
cosmossdk.io/depinject v1.1.0 // indirect
|
||||
cosmossdk.io/errors v1.0.1 // indirect
|
||||
cosmossdk.io/math v1.5.0 // indirect
|
||||
cosmossdk.io/orm v1.0.0-beta.3 // indirect
|
||||
cosmossdk.io/store v1.1.1 // indirect
|
||||
cosmossdk.io/x/circuit v0.1.1 // indirect
|
||||
cosmossdk.io/x/evidence v0.1.1 // indirect
|
||||
cosmossdk.io/x/feegrant v0.1.1 // indirect
|
||||
cosmossdk.io/x/nft v0.1.0 // indirect
|
||||
cosmossdk.io/x/tx v0.13.7 // indirect
|
||||
cosmossdk.io/x/upgrade v0.1.4 // indirect
|
||||
github.com/CosmWasm/wasmvm v1.5.8 // indirect
|
||||
github.com/Oudwins/zog v0.21.6 // indirect
|
||||
github.com/asynkron/protoactor-go v0.0.0-20240822202345-3c0e61ca19c9 // indirect
|
||||
github.com/biter777/countries v1.7.5 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogoproto v1.7.0 // indirect
|
||||
github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.1.1 // indirect
|
||||
github.com/cosmos/ibc-apps/modules/rate-limiting/v8 v8.0.0 // indirect
|
||||
github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect
|
||||
github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.1.1-0.20231213092650-57fcdb9a9a9d // indirect
|
||||
github.com/cosmos/ibc-go/v8 v8.7.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/ethereum/go-ethereum v1.16.3 // indirect
|
||||
github.com/extism/go-sdk v1.7.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/ipfs/boxo v0.32.0 // indirect
|
||||
github.com/ipfs/kubo v0.35.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.4 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/sonr-io/sonr/crypto v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/strangelove-ventures/tokenfactory v0.50.3 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gorm.io/driver/sqlite v1.6.0 // indirect
|
||||
gorm.io/gorm v1.30.1 // indirect
|
||||
nhooyr.io/websocket v1.8.10 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.6.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.9 // indirect
|
||||
cloud.google.com/go/storage v1.41.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
|
||||
github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/Workiva/go-datastructures v1.1.3 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
|
||||
github.com/alexvec/go-bip39 v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.6 // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/btcsuite/btcd v0.24.2 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||
github.com/bwesterb/go-ristretto v1.2.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.6 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/cockroachdb/crlib v0.0.0-20241015224233-894974b3ad94 // indirect
|
||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v1.1.2 // indirect
|
||||
github.com/cockroachdb/pebble/v2 v2.0.3 // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/consensys/gnark-crypto v0.19.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v1.2.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.14.0 // indirect
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect
|
||||
github.com/creachadair/atomicfile v0.3.1 // indirect
|
||||
github.com/creachadair/tomledit v0.0.24 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emicklei/dot v1.6.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/flynn/noise v1.1.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gammazero/deque v1.0.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gogo/status v1.1.0 // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v23.5.26+incompatible // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-getter v1.7.9 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.3 // indirect
|
||||
github.com/hashicorp/go-plugin v1.5.2 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/iancoleman/orderedmap v0.3.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-bitfield v1.1.0 // indirect
|
||||
github.com/ipfs/go-block-format v0.2.1 // indirect
|
||||
github.com/ipfs/go-cid v0.5.0 // indirect
|
||||
github.com/ipfs/go-datastore v0.8.2 // indirect
|
||||
github.com/ipfs/go-ds-measure v0.2.2 // indirect
|
||||
github.com/ipfs/go-fs-lock v0.1.1 // indirect
|
||||
github.com/ipfs/go-ipfs-cmds v0.14.1 // indirect
|
||||
github.com/ipfs/go-ipld-cbor v0.2.0 // indirect
|
||||
github.com/ipfs/go-ipld-format v0.6.1 // indirect
|
||||
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.6.0 // indirect
|
||||
github.com/ipfs/go-metrics-interface v0.3.0 // indirect
|
||||
github.com/ipfs/go-unixfsnode v1.10.1 // indirect
|
||||
github.com/ipld/go-car/v2 v2.14.3 // indirect
|
||||
github.com/ipld/go-codec-dagpb v1.7.0 // indirect
|
||||
github.com/ipld/go-ipld-prime v0.21.0 // indirect
|
||||
github.com/ipshipyard/p2p-forge v0.5.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/koron/go-ssdp v0.0.6 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p v0.43.0 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.33.1 // indirect
|
||||
github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect
|
||||
github.com/libp2p/go-libp2p-record v0.3.1 // indirect
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect
|
||||
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.2 // indirect
|
||||
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||
github.com/linxGnu/grocksdb v1.9.8 // indirect
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
|
||||
github.com/lmittmann/tint v1.0.3 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mholt/acmez/v3 v3.0.0 // indirect
|
||||
github.com/miekg/dns v1.1.66 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
|
||||
github.com/minio/highwayhash v1.0.3 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.16.0 // indirect
|
||||
github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.9.1 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-multistream v0.6.1 // indirect
|
||||
github.com/multiformats/go-varint v0.1.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/orcaman/concurrent-map v1.0.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/pion/datachannel v1.5.10 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.12 // indirect
|
||||
github.com/pion/dtls/v3 v3.0.6 // indirect
|
||||
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||
github.com/pion/interceptor v0.1.40 // indirect
|
||||
github.com/pion/logging v0.2.3 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.15 // indirect
|
||||
github.com/pion/rtp v1.8.19 // indirect
|
||||
github.com/pion/sctp v1.8.39 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.13 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||
github.com/pion/stun v0.6.1 // indirect
|
||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||
github.com/pion/transport/v2 v2.2.10 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/turn/v4 v4.0.2 // indirect
|
||||
github.com/pion/webrtc/v4 v4.1.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/polydawn/refmt v0.89.0 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.64.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/prometheus/tsdb v0.10.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/quic-go/webtransport-go v0.9.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rjeczalik/notify v0.9.3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/samber/lo v1.47.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||
github.com/smarty/assertions v1.15.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
|
||||
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect
|
||||
github.com/whyrusleeping/cbor-gen v0.1.2 // indirect
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/wlynxg/anet v0.0.5 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/dig v1.19.0 // indirect
|
||||
go.uber.org/fx v1.24.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
gonum.org/v1/gonum v0.16.0 // indirect
|
||||
google.golang.org/api v0.186.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
pgregory.net/rapid v1.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||
)
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "snrd",
|
||||
"version": "0.1.0",
|
||||
"description": "Plugin for Sonr PostgreSQL Docker image with pgsodium support and auto-start service",
|
||||
"packages": ["docker@latest"],
|
||||
"env": {
|
||||
"POSTGRES_HOST": "localhost",
|
||||
"POSTGRES_PORT": "${POSTGRES_PORT:-5432}",
|
||||
"POSTGRES_USER": "${POSTGRES_USER:-postgres}",
|
||||
"POSTGRES_PASSWORD": "${POSTGRES_PASSWORD:-password}",
|
||||
"POSTGRES_DB": "${POSTGRES_DB:-sonr}",
|
||||
"PGSODIUM_ROOT_KEY": "${PGSODIUM_ROOT_KEY:-}",
|
||||
"POSTGRES_DATA_DIR": "{{ .Virtenv }}/data",
|
||||
"POSTGRES_DOCKER_IMAGE": "ghcr.io/sonr-io/postgres:latest",
|
||||
"POSTGRES_HOST_AUTH_METHOD": "trust",
|
||||
"POSTGRES_CONTAINER_NAME": "devbox-postgres-sonr"
|
||||
},
|
||||
"create_files": {
|
||||
"{{ .Virtenv }}/data": "",
|
||||
"{{ .Virtenv }}/logs": "",
|
||||
"{{ .Virtenv }}/process-compose.yaml": "etc/process-compose.yaml",
|
||||
"{{ .DevboxDir }}/init.sh": "etc/init.sh"
|
||||
},
|
||||
"shell": {
|
||||
"init_hook": ["chmod +x {{ .DevboxDir }}/init.sh", "{{ .DevboxDir }}/init.sh"],
|
||||
"scripts": {
|
||||
"start": "devbox services start postgres-sonr",
|
||||
"stop": "devbox services stop postgres-sonr",
|
||||
"restart": "devbox services restart postgres-sonr",
|
||||
"logs": "docker logs -f ${POSTGRES_CONTAINER_NAME}",
|
||||
"shell": "docker exec -it ${POSTGRES_CONTAINER_NAME} psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}",
|
||||
"exec": "docker exec -it ${POSTGRES_CONTAINER_NAME} bash",
|
||||
"status": "docker exec ${POSTGRES_CONTAINER_NAME} pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} && echo 'PostgreSQL is ready' || echo 'PostgreSQL is not ready'",
|
||||
"backup": "docker exec ${POSTGRES_CONTAINER_NAME} pg_dumpall -U ${POSTGRES_USER} > {{ .DevboxProjectDir }}/backup-$(date +%Y%m%d-%H%M%S).sql",
|
||||
"reset": [
|
||||
"devbox services stop postgres-sonr",
|
||||
"rm -rf {{ .Virtenv }}/data/*",
|
||||
"devbox services start postgres-sonr"
|
||||
],
|
||||
"generate-key": "openssl rand -hex 32"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
[tool.commitizen]
|
||||
name = "cz_customize"
|
||||
tag_format = "vault/v$version"
|
||||
ignored_tag_formats = ["*/v${version}", "v${version}"]
|
||||
version_scheme = "semver"
|
||||
version_provider = "scm"
|
||||
update_changelog_on_bump = true
|
||||
changelog_file = "CHANGELOG.md"
|
||||
major_version_zero = true
|
||||
annotated_tag = true
|
||||
pre_bump_hooks = ["bash scripts/hook-bump-pre.sh"]
|
||||
post_bump_hooks = ["goreleaser release --clean -f cmd/vault/.goreleaser.yml"]
|
||||
|
||||
[tool.commitizen.customize]
|
||||
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
|
||||
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
|
||||
default_bump = "PATCH"
|
||||
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(vault\\)(!)?:"
|
||||
@@ -1,71 +0,0 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
|
||||
---
|
||||
version: 2
|
||||
dist: dist/vault
|
||||
monorepo:
|
||||
tag_prefix: vault/
|
||||
dir: cmd/vault
|
||||
|
||||
project_name: vault
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
|
||||
builds:
|
||||
- id: vault
|
||||
main: main.go
|
||||
binary: vault
|
||||
no_unique_dist_dir: true
|
||||
hooks:
|
||||
post:
|
||||
- cp dist/vault/vault.wasm x/dwn/client/plugin/vault.wasm
|
||||
- cp dist/vault/vault.wasm packages/es/src/plugin/plugin.wasm
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- wasip1
|
||||
goarch:
|
||||
- wasm
|
||||
flags:
|
||||
- -mod=readonly
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.version={{.Version}}
|
||||
- -X main.commit={{.Commit}}
|
||||
- -X main.date={{.Date}}
|
||||
|
||||
archives:
|
||||
- id: vault-wasm-archive
|
||||
name_template: "vault_wasm_{{ .Version }}"
|
||||
formats: ["binary"]
|
||||
wrap_in_directory: true
|
||||
|
||||
blobs:
|
||||
- provider: s3
|
||||
endpoint: https://eb37925850388bca807b7fab964c12bb.r2.cloudflarestorage.com
|
||||
bucket: releases
|
||||
region: auto
|
||||
directory: "vault/{{ .Tag }}"
|
||||
|
||||
release:
|
||||
disable: false
|
||||
github:
|
||||
owner: sonr-io
|
||||
name: sonr
|
||||
name_template: "{{.ProjectName}}/{{ .Tag }}"
|
||||
draft: false
|
||||
replace_existing_draft: false # Don't replace drafts
|
||||
replace_existing_artifacts: false # Append, don't replace
|
||||
mode: append # Explicitly set to append mode
|
||||
|
||||
checksum:
|
||||
name_template: "vault_checksums.txt"
|
||||
|
||||
snapshot:
|
||||
version_template: "{{ incpatch .Version }}-dev"
|
||||
|
||||
# Changelog configuration
|
||||
changelog:
|
||||
sort: asc
|
||||
@@ -1,6 +0,0 @@
|
||||
## vault/v0.0.2 (2025-10-04)
|
||||
|
||||
## vault/v0.0.1 (2025-10-03)
|
||||
|
||||
|
||||
- feat(vault): introduce assembly of the initial vault
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Output configuration - dual output for both plugin and ES package
|
||||
GIT_ROOT := $(shell git rev-parse --show-toplevel)
|
||||
PLUGIN_DIR := $(GIT_ROOT)/x/dwn/client/plugin
|
||||
ES_PACKAGE_DIR := $(GIT_ROOT)/packages/es/src/plugin
|
||||
OUTPUT_FILE := plugin.wasm
|
||||
PLUGIN_PATH := $(PLUGIN_DIR)/vault.wasm
|
||||
ES_PATH := $(ES_PACKAGE_DIR)/$(OUTPUT_FILE)
|
||||
|
||||
# Build configuration for WASM
|
||||
GOOS := wasip1
|
||||
GOARCH := wasm
|
||||
|
||||
# Version information
|
||||
VERSION := $(shell echo $(shell git describe --tags 2>/dev/null || echo "dev") | sed 's/^v//')
|
||||
COMMIT := $(shell git log -1 --format='%H')
|
||||
|
||||
.PHONY: all build clean install test version help
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
@echo "Building vault WASM module..."
|
||||
@echo "Targets: Plugin and ES package"
|
||||
@mkdir -p $(PLUGIN_DIR)
|
||||
@mkdir -p $(ES_PACKAGE_DIR)
|
||||
@GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(PLUGIN_PATH) main.go
|
||||
@cp $(PLUGIN_PATH) $(ES_PATH)
|
||||
@echo "✅ Vault WASM module built successfully"
|
||||
@echo "Plugin output: $(PLUGIN_PATH)"
|
||||
@echo "ES package output: $(ES_PATH)"
|
||||
@ls -lh $(PLUGIN_PATH) | awk '{print "Plugin size: " $$5}'
|
||||
@ls -lh $(ES_PATH) | awk '{print "ES package size: " $$5}'
|
||||
|
||||
tidy:
|
||||
@echo "Tidying vault build artifacts..."
|
||||
@go mod tidy
|
||||
@echo "✅ Tidy complete"
|
||||
|
||||
test:
|
||||
@echo "Running vault tests..."
|
||||
@go test -v ./...
|
||||
@cd $(GIT_ROOT) && go test -C . -mod=readonly -v github.com/sonr-io/sonr/x/dwn/client/plugin/...
|
||||
|
||||
clean:
|
||||
@echo "Cleaning vault build artifacts..."
|
||||
@rm -f $(PLUGIN_PATH)
|
||||
@rm -f $(ES_PATH)
|
||||
@echo "✅ Clean complete"
|
||||
|
||||
release:
|
||||
@echo "Creating vault release..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/vault/.cz.toml --no-raise 6,21 bump --yes --increment PATCH
|
||||
|
||||
snapshot:
|
||||
@echo "Dry-Run Bumping Vault version..."
|
||||
@cd $(GIT_ROOT) && cz --config cmd/vault/.cz.toml bump --yes --dry-run --no-verify --increment PATCH
|
||||
@echo "Creating vault snapshots for all platforms..."
|
||||
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean -f cmd/vault/.goreleaser.yml
|
||||
|
||||
version:
|
||||
@echo "Vault WASM Module"
|
||||
@echo "================="
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "Commit: $(COMMIT)"
|
||||
@echo "Target OS: $(GOOS)"
|
||||
@echo "Target Arch: $(GOARCH)"
|
||||
@echo "Plugin output: $(PLUGIN_PATH)"
|
||||
@echo "ES output: $(ES_PATH)"
|
||||
|
||||
verify: build
|
||||
@echo "Verifying WASM modules..."
|
||||
@if [ -f "$(PLUGIN_PATH)" ]; then \
|
||||
file "$(PLUGIN_PATH)"; \
|
||||
echo "✅ Plugin WASM file exists"; \
|
||||
else \
|
||||
echo "❌ Plugin WASM file not found"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -f "$(ES_PATH)" ]; then \
|
||||
file "$(ES_PATH)"; \
|
||||
echo "✅ ES package WASM file exists"; \
|
||||
else \
|
||||
echo "❌ ES package WASM file not found"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "Plugin size: $$(du -h $(PLUGIN_PATH) | cut -f1)"
|
||||
@echo "ES size: $$(du -h $(ES_PATH) | cut -f1)"
|
||||
@echo "✅ Verification complete"
|
||||
|
||||
help:
|
||||
@echo "Vault WASM Module Makefile"
|
||||
@echo "=========================="
|
||||
@echo ""
|
||||
@echo "This Makefile builds the vault WebAssembly module for both the"
|
||||
@echo "Sonr blockchain plugin system and the @sonr.io/es package."
|
||||
@echo ""
|
||||
@echo "Available targets:"
|
||||
@echo " build - Build vault WASM module (default)"
|
||||
@echo " clean - Remove built WASM artifacts"
|
||||
@echo " test - Run vault tests"
|
||||
@echo " tidy - Tidy Go module dependencies"
|
||||
@echo " version - Display version information"
|
||||
@echo " verify - Build and verify the WASM modules"
|
||||
@echo " help - Show this help message"
|
||||
@echo ""
|
||||
@echo "Output locations:"
|
||||
@echo " Plugin: $(PLUGIN_PATH)"
|
||||
@echo " ES Package: $(ES_PATH)"
|
||||
@echo ""
|
||||
@echo "Integration:"
|
||||
@echo " The WASM module is built for two purposes:"
|
||||
@echo " 1. Plugin system in x/dwn/client/plugin"
|
||||
@echo " 2. ES package for browser distribution via jsDelivr"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make build # Build WASM module to both locations"
|
||||
@echo " make verify # Build and verify both outputs"
|
||||
@echo " make clean # Remove all artifacts"
|
||||
@@ -1,477 +0,0 @@
|
||||
# Vault - WebAssembly Vault Plugin
|
||||
|
||||
Vault is a WebAssembly-based vault system for the Sonr blockchain that provides secure, isolated execution of cryptographic operations. Built using the Extism framework, Vault enables secure multi-party computation (MPC) and vault management within a sandboxed WebAssembly environment.
|
||||
|
||||
## Overview
|
||||
|
||||
Vault serves as a cryptographic vault system that:
|
||||
|
||||
- Provides secure enclave-based key generation and management
|
||||
- Supports multi-chain transaction signing (Cosmos, EVM)
|
||||
- Implements WebAuthn-based authentication
|
||||
- Offers secure import/export functionality via IPFS
|
||||
- Enables isolated execution through WebAssembly
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
- **MPC Enclave**: Multi-party computation system for secure key operations
|
||||
- **Vault Management**: Create, unlock, and manage cryptographic vaults
|
||||
- **IPFS Integration**: Secure backup and restore of encrypted vault data
|
||||
- **WebAuthn Support**: Passwordless authentication for vault operations
|
||||
- **Multi-Chain Support**: Transaction signing for different blockchain networks
|
||||
|
||||
### Build Configuration
|
||||
|
||||
Vault is built specifically for WebAssembly:
|
||||
|
||||
```go
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core Enclave Operations
|
||||
|
||||
#### `generate`
|
||||
|
||||
```go
|
||||
//go:wasmexport generate
|
||||
func generate() int32
|
||||
```
|
||||
|
||||
Creates a new MPC enclave and returns the enclave data and public key.
|
||||
|
||||
**Input**: `GenerateRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `GenerateResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"data": "EnclaveData",
|
||||
"public_key": "[]byte"
|
||||
}
|
||||
```
|
||||
|
||||
#### `refresh`
|
||||
|
||||
```go
|
||||
//go:wasmexport refresh
|
||||
func refresh() int32
|
||||
```
|
||||
|
||||
Refreshes an existing enclave with new cryptographic material.
|
||||
|
||||
**Input**: `RefreshRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"enclave": "EnclaveData"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `RefreshResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"okay": "bool",
|
||||
"data": "EnclaveData"
|
||||
}
|
||||
```
|
||||
|
||||
#### `sign`
|
||||
|
||||
```go
|
||||
//go:wasmexport sign
|
||||
func sign() int32
|
||||
```
|
||||
|
||||
Signs a message using the enclave's private key.
|
||||
|
||||
**Input**: `SignRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "[]byte",
|
||||
"enclave": "EnclaveData"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `SignResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"signature": "[]byte"
|
||||
}
|
||||
```
|
||||
|
||||
#### `verify`
|
||||
|
||||
```go
|
||||
//go:wasmexport verify
|
||||
func verify() int32
|
||||
```
|
||||
|
||||
Verifies a signature against a message and public key.
|
||||
|
||||
**Input**: `VerifyRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"public_key": "[]byte",
|
||||
"message": "[]byte",
|
||||
"signature": "[]byte"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `VerifyResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": "bool"
|
||||
}
|
||||
```
|
||||
|
||||
### Vault Import/Export Operations
|
||||
|
||||
#### `export`
|
||||
|
||||
```go
|
||||
//go:wasmexport export
|
||||
func export() int32
|
||||
```
|
||||
|
||||
Encrypts and exports vault data to IPFS, returning a Content ID (CID).
|
||||
|
||||
**Input**: `ExportRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"enclave": "EnclaveData",
|
||||
"password": "[]byte"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `ExportResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "string",
|
||||
"success": "bool"
|
||||
}
|
||||
```
|
||||
|
||||
#### `import`
|
||||
|
||||
```go
|
||||
//go:wasmexport import
|
||||
func importVault() int32
|
||||
```
|
||||
|
||||
Retrieves and decrypts vault data from IPFS using a CID and password.
|
||||
|
||||
**Input**: `ImportRequest`
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "string",
|
||||
"password": "[]byte"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**: `ImportResponse`
|
||||
|
||||
```json
|
||||
{
|
||||
"enclave": "EnclaveData",
|
||||
"success": "bool"
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Vault Operations
|
||||
|
||||
#### `create_vault_enclave`
|
||||
|
||||
```go
|
||||
//go:wasmexport create_vault_enclave
|
||||
func createVaultEnclave() int32
|
||||
```
|
||||
|
||||
Creates a new vault enclave with advanced configuration options.
|
||||
|
||||
**Input**: `EnclaveConfig`
|
||||
|
||||
```json
|
||||
{
|
||||
"vault_id": "string",
|
||||
"key_derivation_method": "string",
|
||||
"encryption_algorithm": "string",
|
||||
"signing_algorithm": "string",
|
||||
"webauthn_enabled": "bool",
|
||||
"auto_lock_timeout": "int64",
|
||||
"key_rotation_interval": "int64",
|
||||
"supported_chains": ["string"],
|
||||
"max_concurrent_ops": "int",
|
||||
"memory_limit": "uint64"
|
||||
}
|
||||
```
|
||||
|
||||
#### `unlock_vault_enclave`
|
||||
|
||||
```go
|
||||
//go:wasmexport unlock_vault_enclave
|
||||
func unlockVaultEnclave() int32
|
||||
```
|
||||
|
||||
Unlocks a vault enclave, optionally using WebAuthn authentication.
|
||||
|
||||
#### `lock_vault_enclave`
|
||||
|
||||
```go
|
||||
//go:wasmexport lock_vault_enclave
|
||||
func lockVaultEnclave() int32
|
||||
```
|
||||
|
||||
Locks a vault enclave to prevent unauthorized access.
|
||||
|
||||
#### `rotate_vault_key`
|
||||
|
||||
```go
|
||||
//go:wasmexport rotate_vault_key
|
||||
func rotateVaultKey() int32
|
||||
```
|
||||
|
||||
Rotates the cryptographic keys within a vault enclave.
|
||||
|
||||
### Multi-Chain Transaction Signing
|
||||
|
||||
#### `sign_cosmos_transaction`
|
||||
|
||||
```go
|
||||
//go:wasmexport sign_cosmos_transaction
|
||||
func signCosmosTransaction() int32
|
||||
```
|
||||
|
||||
Signs transactions for Cosmos SDK-based blockchains.
|
||||
|
||||
#### `sign_evm_transaction`
|
||||
|
||||
```go
|
||||
//go:wasmexport sign_evm_transaction
|
||||
func signEvmTransaction() int32
|
||||
```
|
||||
|
||||
Signs transactions for Ethereum Virtual Machine compatible chains.
|
||||
|
||||
#### `sign_message`
|
||||
|
||||
```go
|
||||
//go:wasmexport sign_message
|
||||
func signMessage() int32
|
||||
```
|
||||
|
||||
Signs arbitrary messages using the vault's private key.
|
||||
|
||||
### Health and Monitoring
|
||||
|
||||
#### `get_vault_health`
|
||||
|
||||
```go
|
||||
//go:wasmexport get_vault_health
|
||||
func getVaultHealth() int32
|
||||
```
|
||||
|
||||
Returns the health status of a vault enclave.
|
||||
|
||||
**Output**: `EnclaveHealth`
|
||||
|
||||
```json
|
||||
{
|
||||
"vault_id": "string",
|
||||
"status": "string",
|
||||
"last_activity": "int64",
|
||||
"key_rotation_due": "bool",
|
||||
"attestation_valid": "bool"
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Motor supports configuration through Extism variables:
|
||||
|
||||
- `chain_id`: Blockchain network identifier (default: "sonr-testnet-1")
|
||||
- `password`: Default password for enclave operations (default: "password")
|
||||
- `gateway`: IPFS gateway URL (default: "https://ipfs.did.run/ipfs/")
|
||||
|
||||
Access these via helper functions:
|
||||
|
||||
```go
|
||||
func GetChainID() string
|
||||
func GetPassword() []byte
|
||||
func GetGateway() string
|
||||
```
|
||||
|
||||
### IPFS Integration
|
||||
|
||||
Motor integrates with IPFS for secure vault backup and restore:
|
||||
|
||||
- **Storage Endpoint**: `http://127.0.0.1:5001/api/v0/add`
|
||||
- **Retrieval Endpoint**: `http://127.0.0.1:5001/api/v0/cat`
|
||||
- **Data Format**: Encrypted vault data stored as content-addressed objects
|
||||
- **Security**: All vault data is encrypted before IPFS storage
|
||||
|
||||
## Security Features
|
||||
|
||||
### Enclave Isolation
|
||||
|
||||
- WebAssembly sandbox provides memory isolation
|
||||
- Secure execution environment prevents side-channel attacks
|
||||
- Attestation mechanisms ensure enclave integrity
|
||||
|
||||
### Authentication
|
||||
|
||||
- WebAuthn support for passwordless authentication
|
||||
- Challenge-response authentication flows
|
||||
- Automatic vault locking with configurable timeouts
|
||||
|
||||
### Key Management
|
||||
|
||||
- Multi-party computation for enhanced security
|
||||
- Automatic key rotation with configurable intervals
|
||||
- Secure key derivation and storage
|
||||
|
||||
### Data Protection
|
||||
|
||||
- AES encryption for sensitive data
|
||||
- Password-based encryption for import/export
|
||||
- Secure memory handling within WASM environment
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Enclave Operations
|
||||
|
||||
```javascript
|
||||
// Generate new enclave
|
||||
const generateReq = { id: "my-vault" };
|
||||
const result = call_wasm_function("generate", generateReq);
|
||||
|
||||
// Sign a message
|
||||
const signReq = {
|
||||
message: new Uint8Array([1, 2, 3, 4]),
|
||||
enclave: result.data,
|
||||
};
|
||||
const signature = call_wasm_function("sign", signReq);
|
||||
```
|
||||
|
||||
### Vault Management
|
||||
|
||||
```javascript
|
||||
// Create vault with configuration
|
||||
const config = {
|
||||
vault_id: "user-vault-001",
|
||||
webauthn_enabled: true,
|
||||
auto_lock_timeout: 300,
|
||||
supported_chains: ["cosmos", "ethereum"],
|
||||
};
|
||||
const vault = call_wasm_function("create_vault_enclave", config);
|
||||
|
||||
// Sign Cosmos transaction
|
||||
const cosmosReq = {
|
||||
vault_id: "user-vault-001",
|
||||
chain_type: "cosmos",
|
||||
chain_id: "cosmoshub-4",
|
||||
message: transactionBytes,
|
||||
};
|
||||
const cosmosResult = call_wasm_function("sign_cosmos_transaction", cosmosReq);
|
||||
```
|
||||
|
||||
### Import/Export Operations
|
||||
|
||||
```javascript
|
||||
// Export vault to IPFS
|
||||
const exportReq = {
|
||||
enclave: vaultData,
|
||||
password: new Uint8Array([
|
||||
/* password bytes */
|
||||
]),
|
||||
};
|
||||
const exportResult = call_wasm_function("export", exportReq);
|
||||
console.log("Vault exported to CID:", exportResult.cid);
|
||||
|
||||
// Import vault from IPFS
|
||||
const importReq = {
|
||||
cid: "QmXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx",
|
||||
password: new Uint8Array([
|
||||
/* password bytes */
|
||||
]),
|
||||
};
|
||||
const importResult = call_wasm_function("import", importReq);
|
||||
```
|
||||
|
||||
## Building and Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.24.4+
|
||||
- Extism runtime
|
||||
- IPFS node (for import/export functionality)
|
||||
|
||||
### Build Commands
|
||||
|
||||
```bash
|
||||
# Build WebAssembly module
|
||||
GOOS=js GOARCH=wasm go build -o motr.wasm main.go
|
||||
|
||||
# Build via Makefile
|
||||
make motr
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
Motor is designed to be integrated with:
|
||||
|
||||
- **Highway Service**: PostgreSQL-backed HTTP API
|
||||
- **Sonr Blockchain**: Cosmos SDK-based blockchain node
|
||||
- **IPFS Network**: Decentralized storage system
|
||||
- **WebAuthn Infrastructure**: Passwordless authentication
|
||||
|
||||
## Error Handling
|
||||
|
||||
All functions return `int32` status codes:
|
||||
|
||||
- `0`: Success
|
||||
- `1`: Error (details available via `pdk.SetError`)
|
||||
|
||||
Error information is logged using Extism's logging system:
|
||||
|
||||
```go
|
||||
pdk.Log(pdk.LogError, "Error message")
|
||||
pdk.Log(pdk.LogInfo, "Info message")
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Core Dependencies
|
||||
|
||||
- `github.com/extism/go-pdk`: WebAssembly plugin development kit
|
||||
- `github.com/sonr-io/sonr/crypto/mpc`: Multi-party computation library
|
||||
|
||||
### Cryptographic Libraries
|
||||
|
||||
- `filippo.io/edwards25519`: Edwards25519 elliptic curve
|
||||
- `github.com/btcsuite/btcd/btcec/v2`: Bitcoin cryptography
|
||||
- `github.com/consensys/gnark-crypto`: Zero-knowledge proof cryptography
|
||||
|
||||
## License
|
||||
|
||||
Motor is part of the Sonr blockchain project. See the project's main license for terms and conditions.
|
||||
@@ -1,26 +0,0 @@
|
||||
module vault
|
||||
|
||||
go 1.24.7
|
||||
|
||||
replace github.com/sonr-io/sonr/crypto => ../../crypto/
|
||||
|
||||
require (
|
||||
github.com/extism/go-pdk v1.1.3
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/sonr-io/sonr/crypto v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
|
||||
github.com/bwesterb/go-ristretto v1.2.3 // indirect
|
||||
github.com/consensys/gnark-crypto v0.19.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
)
|
||||
@@ -1,38 +0,0 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM=
|
||||
github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA=
|
||||
github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
||||
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
|
||||
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1,483 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/extism/go-pdk"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/sonr-io/sonr/crypto/mpc"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyChainID = "chain_id"
|
||||
KeyEnclave = "enclave"
|
||||
KeyVaultConfig = "vault_config"
|
||||
)
|
||||
|
||||
// GetChainID returns the chain ID to use for unlocking the enclave
|
||||
func GetChainID() string {
|
||||
v := pdk.GetVar(KeyChainID)
|
||||
if v == nil {
|
||||
return "sonr-testnet-1"
|
||||
}
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// GetEnclaveData loads MPC enclave data from PDK environment
|
||||
func GetEnclaveData() (*mpc.EnclaveData, error) {
|
||||
v := pdk.GetVar(KeyEnclave)
|
||||
if v == nil {
|
||||
return nil, fmt.Errorf("enclave data not provided in environment")
|
||||
}
|
||||
|
||||
var data mpc.EnclaveData
|
||||
if err := json.Unmarshal(v, &data); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal enclave data: %w", err)
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
// GetVaultConfig loads vault configuration from PDK environment
|
||||
func GetVaultConfig() map[string]any {
|
||||
v := pdk.GetVar(KeyVaultConfig)
|
||||
if v == nil {
|
||||
return make(map[string]any)
|
||||
}
|
||||
|
||||
var config map[string]any
|
||||
if err := json.Unmarshal(v, &config); err != nil {
|
||||
pdk.Log(pdk.LogWarn, fmt.Sprintf("Failed to parse vault config: %v", err))
|
||||
return make(map[string]any)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// UCAN Token Request/Response types
|
||||
type NewOriginTokenRequest struct {
|
||||
AudienceDID string `json:"audience_did"`
|
||||
Attenuations []map[string]any `json:"attenuations,omitempty"`
|
||||
Facts []string `json:"facts,omitempty"`
|
||||
NotBefore int64 `json:"not_before,omitempty"`
|
||||
ExpiresAt int64 `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
type NewAttenuatedTokenRequest struct {
|
||||
ParentToken string `json:"parent_token"`
|
||||
AudienceDID string `json:"audience_did"`
|
||||
Attenuations []map[string]any `json:"attenuations,omitempty"`
|
||||
Facts []string `json:"facts,omitempty"`
|
||||
NotBefore int64 `json:"not_before,omitempty"`
|
||||
ExpiresAt int64 `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
type UCANTokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
Issuer string `json:"issuer"`
|
||||
Address string `json:"address"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type SignDataRequest struct {
|
||||
Data []byte `json:"data"`
|
||||
}
|
||||
|
||||
type SignDataResponse struct {
|
||||
Signature []byte `json:"signature"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type VerifyDataRequest struct {
|
||||
Data []byte `json:"data"`
|
||||
Signature []byte `json:"signature"`
|
||||
}
|
||||
|
||||
type VerifyDataResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type GetIssuerDIDResponse struct {
|
||||
IssuerDID string `json:"issuer_did"`
|
||||
Address string `json:"address"`
|
||||
ChainCode string `json:"chain_code"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
enclave mpc.Enclave
|
||||
issuerDID string
|
||||
address string
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Initialize MPC enclave from PDK environment
|
||||
if err := initializeEnclave(); err != nil {
|
||||
pdk.SetError(fmt.Errorf("failed to initialize enclave: %w", err))
|
||||
return
|
||||
}
|
||||
pdk.Log(pdk.LogInfo, "Motor plugin initialized as MPC-based UCAN source")
|
||||
}
|
||||
|
||||
// initializeEnclave initializes the MPC enclave from PDK environment
|
||||
func initializeEnclave() error {
|
||||
// Load enclave data from PDK environment
|
||||
enclaveData, err := GetEnclaveData()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get enclave data: %w", err)
|
||||
}
|
||||
|
||||
// Import MPC enclave from data
|
||||
enclave, err = mpc.ImportEnclave(mpc.WithEnclaveData(enclaveData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to import enclave: %w", err)
|
||||
}
|
||||
|
||||
// Derive issuer DID and address from enclave public key
|
||||
pubKeyBytes := enclave.PubKeyBytes()
|
||||
issuerDID, address, err = deriveIssuerDIDFromBytes(pubKeyBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to derive issuer DID: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:wasmexport new_origin_token
|
||||
func newOriginToken() int32 {
|
||||
if !enclave.IsValid() {
|
||||
pdk.SetError(fmt.Errorf("enclave not initialized"))
|
||||
return 1
|
||||
}
|
||||
|
||||
req := &NewOriginTokenRequest{}
|
||||
err := pdk.InputJSON(req)
|
||||
if err != nil {
|
||||
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Convert timestamps
|
||||
var notBefore, expiresAt time.Time
|
||||
if req.NotBefore > 0 {
|
||||
notBefore = time.Unix(req.NotBefore, 0)
|
||||
}
|
||||
if req.ExpiresAt > 0 {
|
||||
expiresAt = time.Unix(req.ExpiresAt, 0)
|
||||
}
|
||||
|
||||
// Create origin token using MPC signing
|
||||
tokenString, err := createUCANToken(
|
||||
req.AudienceDID,
|
||||
nil,
|
||||
req.Attenuations,
|
||||
req.Facts,
|
||||
notBefore,
|
||||
expiresAt,
|
||||
)
|
||||
if err != nil {
|
||||
resp := &UCANTokenResponse{Error: err.Error()}
|
||||
pdk.OutputJSON(resp)
|
||||
return 1
|
||||
}
|
||||
|
||||
resp := &UCANTokenResponse{
|
||||
Token: tokenString,
|
||||
Issuer: issuerDID,
|
||||
Address: address,
|
||||
}
|
||||
pdk.OutputJSON(resp)
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:wasmexport new_attenuated_token
|
||||
func newAttenuatedToken() int32 {
|
||||
if !enclave.IsValid() {
|
||||
pdk.SetError(fmt.Errorf("enclave not initialized"))
|
||||
return 1
|
||||
}
|
||||
|
||||
req := &NewAttenuatedTokenRequest{}
|
||||
err := pdk.InputJSON(req)
|
||||
if err != nil {
|
||||
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Convert timestamps
|
||||
var notBefore, expiresAt time.Time
|
||||
if req.NotBefore > 0 {
|
||||
notBefore = time.Unix(req.NotBefore, 0)
|
||||
}
|
||||
if req.ExpiresAt > 0 {
|
||||
expiresAt = time.Unix(req.ExpiresAt, 0)
|
||||
}
|
||||
|
||||
// Create proofs from parent token
|
||||
proofs := []string{req.ParentToken}
|
||||
|
||||
// Create attenuated token using MPC signing
|
||||
tokenString, err := createUCANToken(
|
||||
req.AudienceDID,
|
||||
proofs,
|
||||
req.Attenuations,
|
||||
req.Facts,
|
||||
notBefore,
|
||||
expiresAt,
|
||||
)
|
||||
if err != nil {
|
||||
resp := &UCANTokenResponse{Error: err.Error()}
|
||||
pdk.OutputJSON(resp)
|
||||
return 1
|
||||
}
|
||||
|
||||
resp := &UCANTokenResponse{
|
||||
Token: tokenString,
|
||||
Issuer: issuerDID,
|
||||
Address: address,
|
||||
}
|
||||
pdk.OutputJSON(resp)
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:wasmexport sign_data
|
||||
func signData() int32 {
|
||||
if !enclave.IsValid() {
|
||||
pdk.SetError(fmt.Errorf("enclave not initialized"))
|
||||
return 1
|
||||
}
|
||||
|
||||
req := &SignDataRequest{}
|
||||
err := pdk.InputJSON(req)
|
||||
if err != nil {
|
||||
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Sign data using MPC enclave
|
||||
signature, err := enclave.Sign(req.Data)
|
||||
if err != nil {
|
||||
resp := &SignDataResponse{Error: err.Error()}
|
||||
pdk.OutputJSON(resp)
|
||||
return 1
|
||||
}
|
||||
|
||||
resp := &SignDataResponse{Signature: signature}
|
||||
pdk.OutputJSON(resp)
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:wasmexport verify_data
|
||||
func verifyData() int32 {
|
||||
if !enclave.IsValid() {
|
||||
pdk.SetError(fmt.Errorf("enclave not initialized"))
|
||||
return 1
|
||||
}
|
||||
|
||||
req := &VerifyDataRequest{}
|
||||
err := pdk.InputJSON(req)
|
||||
if err != nil {
|
||||
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Verify data using MPC enclave
|
||||
valid, err := enclave.Verify(req.Data, req.Signature)
|
||||
if err != nil {
|
||||
resp := &VerifyDataResponse{Error: err.Error()}
|
||||
pdk.OutputJSON(resp)
|
||||
return 1
|
||||
}
|
||||
|
||||
resp := &VerifyDataResponse{Valid: valid}
|
||||
pdk.OutputJSON(resp)
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:wasmexport get_issuer_did
|
||||
func getIssuerDID() int32 {
|
||||
if !enclave.IsValid() {
|
||||
pdk.SetError(fmt.Errorf("enclave not initialized"))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get chain code for deterministic derivation
|
||||
chainCode, err := getChainCode()
|
||||
if err != nil {
|
||||
resp := &GetIssuerDIDResponse{Error: err.Error()}
|
||||
pdk.OutputJSON(resp)
|
||||
return 1
|
||||
}
|
||||
|
||||
resp := &GetIssuerDIDResponse{
|
||||
IssuerDID: issuerDID,
|
||||
Address: address,
|
||||
ChainCode: fmt.Sprintf("%x", chainCode),
|
||||
}
|
||||
pdk.OutputJSON(resp)
|
||||
return 0
|
||||
}
|
||||
|
||||
// UCAN token creation and MPC signing implementation
|
||||
|
||||
// MPCSigningMethod implements JWT signing using MPC enclaves
|
||||
type MPCSigningMethod struct {
|
||||
Name string
|
||||
enclave mpc.Enclave
|
||||
}
|
||||
|
||||
// Alg returns the signing method algorithm name
|
||||
func (m *MPCSigningMethod) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Sign signs a JWT string using the MPC enclave
|
||||
func (m *MPCSigningMethod) Sign(signingString string, key any) ([]byte, error) {
|
||||
// Hash the signing string
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
digest := hasher.Sum(nil)
|
||||
|
||||
// Use MPC enclave to sign the digest
|
||||
sig, err := m.enclave.Sign(digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign with MPC: %w", err)
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// Verify verifies a JWT signature using the MPC enclave
|
||||
func (m *MPCSigningMethod) Verify(signingString string, sig []byte, key any) error {
|
||||
// Hash the signing string
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
digest := hasher.Sum(nil)
|
||||
|
||||
// Use MPC enclave to verify signature
|
||||
valid, err := m.enclave.Verify(digest, sig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to verify signature: %w", err)
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return fmt.Errorf("signature verification failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createUCANToken creates a UCAN token using MPC signing
|
||||
func createUCANToken(
|
||||
audienceDID string,
|
||||
proofs []string,
|
||||
attenuations []map[string]any,
|
||||
facts []string,
|
||||
notBefore, expiresAt time.Time,
|
||||
) (string, error) {
|
||||
// Validate audience DID
|
||||
if audienceDID == "" {
|
||||
return "", fmt.Errorf("audience DID is required")
|
||||
}
|
||||
|
||||
// Create MPC signing method
|
||||
signingMethod := &MPCSigningMethod{
|
||||
Name: "MPC256",
|
||||
enclave: enclave,
|
||||
}
|
||||
|
||||
// Create JWT token
|
||||
token := jwt.New(signingMethod)
|
||||
|
||||
// Set UCAN version in header
|
||||
token.Header["ucv"] = "0.9.0"
|
||||
|
||||
// Prepare time claims
|
||||
var nbfUnix, expUnix int64
|
||||
if !notBefore.IsZero() {
|
||||
nbfUnix = notBefore.Unix()
|
||||
}
|
||||
if !expiresAt.IsZero() {
|
||||
expUnix = expiresAt.Unix()
|
||||
}
|
||||
|
||||
// Set claims
|
||||
claims := jwt.MapClaims{
|
||||
"iss": issuerDID,
|
||||
"aud": audienceDID,
|
||||
}
|
||||
|
||||
// Add attenuations if provided
|
||||
if len(attenuations) > 0 {
|
||||
claims["att"] = attenuations
|
||||
}
|
||||
|
||||
// Add proofs if provided
|
||||
if len(proofs) > 0 {
|
||||
claims["prf"] = proofs
|
||||
}
|
||||
|
||||
// Add facts if provided
|
||||
if len(facts) > 0 {
|
||||
claims["fct"] = facts
|
||||
}
|
||||
|
||||
// Add time claims
|
||||
if nbfUnix > 0 {
|
||||
claims["nbf"] = nbfUnix
|
||||
}
|
||||
if expUnix > 0 {
|
||||
claims["exp"] = expUnix
|
||||
}
|
||||
|
||||
token.Claims = claims
|
||||
|
||||
// Sign the token using MPC enclave (key parameter is ignored for MPC signing)
|
||||
tokenString, err := token.SignedString(nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sign token with MPC: %w", err)
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// deriveIssuerDIDFromBytes creates issuer DID and address from public key bytes
|
||||
func deriveIssuerDIDFromBytes(pubKeyBytes []byte) (string, string, error) {
|
||||
if len(pubKeyBytes) == 0 {
|
||||
return "", "", fmt.Errorf("empty public key bytes")
|
||||
}
|
||||
|
||||
// Generate address from public key (simplified implementation)
|
||||
address := fmt.Sprintf("sonr1%x", pubKeyBytes[:20])
|
||||
|
||||
// Create DID from address (simplified implementation)
|
||||
issuerDID := fmt.Sprintf("did:sonr:%s", address)
|
||||
|
||||
return issuerDID, address, nil
|
||||
}
|
||||
|
||||
// getChainCode derives a deterministic chain code from the enclave
|
||||
func getChainCode() ([]byte, error) {
|
||||
if !enclave.IsValid() {
|
||||
return nil, fmt.Errorf("enclave is not valid")
|
||||
}
|
||||
|
||||
// Sign the address to create a deterministic chain code
|
||||
sig, err := enclave.Sign([]byte(address))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign address for chain code: %w", err)
|
||||
}
|
||||
|
||||
// Hash the signature to create a 32-byte chain code
|
||||
hasher := sha256.New()
|
||||
hasher.Write(sig)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
return hash[:32], nil
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package main
|
||||
|
||||
// Version is set by commitizen during release process
|
||||
var Version = "dev"
|
||||
Reference in New Issue
Block a user