Last version before deleting Makefile and CmakeLists.txt
This commit is contained in:
parent
6014a250dd
commit
8ed9262c65
30
Makefile
30
Makefile
|
@ -14,10 +14,10 @@ ifneq ($(ENABLE_LOGGING),)
|
|||
endif
|
||||
|
||||
# Include paths
|
||||
INCLUDES = -I. -Inostr_core -Icjson -Isecp256k1/include -Inostr_websocket -I./openssl-install/include
|
||||
INCLUDES = -I. -Inostr_core -Inostr_core/crypto -Icjson -Isecp256k1/include -Inostr_websocket -I./openssl-install/include
|
||||
|
||||
# Library source files
|
||||
LIB_SOURCES = nostr_core/core.c nostr_core/core_relays.c nostr_core/nostr_crypto.c nostr_core/nostr_secp256k1.c nostr_core/nostr_aes.c nostr_core/nostr_chacha20.c nostr_websocket/nostr_websocket_openssl.c cjson/cJSON.c
|
||||
# Library source files - modular NIP-based structure
|
||||
LIB_SOURCES = nostr_core/crypto/nostr_crypto.c nostr_core/crypto/nostr_secp256k1.c nostr_core/crypto/nostr_aes.c nostr_core/crypto/nostr_chacha20.c cjson/cJSON.c nostr_core/utils.c nostr_core/nip001.c nostr_core/nip005.c nostr_core/nip006.c nostr_core/nip011.c nostr_core/nip013.c nostr_core/nip019.c
|
||||
LIB_OBJECTS = $(LIB_SOURCES:.c=.o)
|
||||
ARM64_LIB_OBJECTS = $(LIB_SOURCES:.c=.arm64.o)
|
||||
|
||||
|
@ -243,11 +243,11 @@ ARM64_CORE_TEST_EXEC = tests/nostr_core_test_arm64
|
|||
ARM64_RELAY_POOL_TEST_EXEC = tests/relay_pool_test_arm64
|
||||
ARM64_NIP04_TEST_EXEC = tests/nip04_test_arm64
|
||||
|
||||
# Test compilation flags
|
||||
TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include -DDISABLE_NIP05
|
||||
TEST_LDFLAGS = -L. -lnostr_core -lm -static
|
||||
ARM64_TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -DDISABLE_NIP05
|
||||
ARM64_TEST_LDFLAGS = -L. -lnostr_core_arm64 -lssl -lcrypto -lm -static
|
||||
# Test compilation flags - matches exact customer usage pattern
|
||||
TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include
|
||||
TEST_LDFLAGS = ./libnostr_core.a -lm -static
|
||||
ARM64_TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I.
|
||||
ARM64_TEST_LDFLAGS = ./libnostr_core_arm64.a -lm -static
|
||||
|
||||
# Build crypto test executable (x86_64)
|
||||
$(CRYPTO_TEST_EXEC): tests/nostr_crypto_test.c $(STATIC_LIB)
|
||||
|
@ -279,10 +279,10 @@ $(NIP04_TEST_EXEC): tests/nip04_test.c $(STATIC_LIB)
|
|||
@echo "Building NIP-04 encryption test suite (x86_64)..."
|
||||
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
# Build HTTP test executable (x86_64) - Uses static curl for compatibility testing
|
||||
$(HTTP_TEST_EXEC): tests/http_test.c
|
||||
# Build HTTP test executable (x86_64) - Uses customer linking pattern
|
||||
$(HTTP_TEST_EXEC): tests/http_test.c $(STATIC_LIB)
|
||||
@echo "Building HTTP/curl compatibility test (x86_64)..."
|
||||
$(CC) $(TEST_CFLAGS) -I./curl-install/include $< -o $@ ./curl-install/lib/libcurl.a -lssl -lcrypto -lz -ldl -lpthread -static
|
||||
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
# Build WebSocket SSL test executable (x86_64)
|
||||
$(WSS_TEST_EXEC): tests/wss_test.c $(STATIC_LIB)
|
||||
|
@ -299,15 +299,15 @@ $(MAKEFILE_STATIC_TEST_EXEC): tests/makefile_static_test.c
|
|||
@echo "Building Makefile-based static configuration test (x86_64)..."
|
||||
$(CC) $(TEST_CFLAGS) $< -o $@ -static
|
||||
|
||||
# Build NIP-05 test executable (x86_64) - NIP-05 enabled with static curl
|
||||
# Build NIP-05 test executable (x86_64) - Uses customer linking pattern
|
||||
$(NIP05_TEST_EXEC): tests/nip05_test.c $(STATIC_LIB)
|
||||
@echo "Building NIP-05 identifier verification test (x86_64)..."
|
||||
$(CC) -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include -I./curl-install/include $< -o $@ -L. -L./openssl-install/lib64 -lnostr_core ./curl-install/lib/libcurl.a ./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a -lz -ldl -lpthread -lm -static
|
||||
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
# Build NIP-11 test executable (x86_64) - NIP-11 enabled with static curl
|
||||
# Build NIP-11 test executable (x86_64) - Uses customer linking pattern
|
||||
$(NIP11_TEST_EXEC): tests/nip11_test.c $(STATIC_LIB)
|
||||
@echo "Building NIP-11 relay information test (x86_64)..."
|
||||
$(CC) -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include -I./curl-install/include $< -o $@ -L. -L./openssl-install/lib64 -lnostr_core ./curl-install/lib/libcurl.a ./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a -lz -ldl -lpthread -lm -static
|
||||
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
# Build simple initialization test executable (x86_64)
|
||||
tests/simple_init_test: tests/simple_init_test.c $(STATIC_LIB)
|
||||
|
|
692
build.sh
692
build.sh
|
@ -1,10 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
# NOSTR Core Library Build Script
|
||||
# Provides convenient build targets for the standalone library
|
||||
# Automatically increments patch version with each build
|
||||
# NOSTR Core Library - Customer Build Script with Auto-Detection
|
||||
# Automatically detects which NIPs are needed based on #include statements
|
||||
|
||||
set -e # Exit on any error
|
||||
set -e # Exit on error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
|
@ -14,7 +13,7 @@ BLUE='\033[0;34m'
|
|||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
|
@ -30,365 +29,374 @@ print_error() {
|
|||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to automatically increment version
|
||||
increment_version() {
|
||||
print_status "Incrementing version..."
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
print_warning "Not in a git repository - skipping version increment"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Get the highest version tag (not necessarily the most recent chronologically)
|
||||
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0")
|
||||
if [[ -z "$LATEST_TAG" ]]; then
|
||||
LATEST_TAG="v0.1.0"
|
||||
fi
|
||||
|
||||
# Extract version components (remove 'v' prefix if present)
|
||||
VERSION=${LATEST_TAG#v}
|
||||
|
||||
# Parse major.minor.patch
|
||||
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
MAJOR=${BASH_REMATCH[1]}
|
||||
MINOR=${BASH_REMATCH[2]}
|
||||
PATCH=${BASH_REMATCH[3]}
|
||||
else
|
||||
print_error "Invalid version format in tag: $LATEST_TAG"
|
||||
print_error "Expected format: v0.1.0"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Increment patch version
|
||||
NEW_PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
|
||||
|
||||
print_status "Current version: $LATEST_TAG"
|
||||
print_status "New version: $NEW_VERSION"
|
||||
|
||||
# Create new git tag
|
||||
if git tag "$NEW_VERSION" 2>/dev/null; then
|
||||
print_success "Created new version tag: $NEW_VERSION"
|
||||
else
|
||||
print_warning "Tag $NEW_VERSION already exists - using existing version"
|
||||
NEW_VERSION=$LATEST_TAG
|
||||
fi
|
||||
|
||||
# Update VERSION file for compatibility
|
||||
echo "${NEW_VERSION#v}" > VERSION
|
||||
print_success "Updated VERSION file to ${NEW_VERSION#v}"
|
||||
}
|
||||
|
||||
# Function to perform git operations after successful build
|
||||
perform_git_operations() {
|
||||
local commit_message="$1"
|
||||
|
||||
if [[ -z "$commit_message" ]]; then
|
||||
return 0 # No commit message provided, skip git operations
|
||||
fi
|
||||
|
||||
print_status "Performing git operations..."
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
print_warning "Not in a git repository - skipping git operations"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
print_warning "No changes to commit"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add all changes
|
||||
print_status "Adding changes to git..."
|
||||
if ! git add .; then
|
||||
print_error "Failed to add changes to git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Commit changes
|
||||
print_status "Committing changes with message: '$commit_message'"
|
||||
if ! git commit -m "$commit_message"; then
|
||||
print_error "Failed to commit changes"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Push changes
|
||||
print_status "Pushing changes to remote repository..."
|
||||
if ! git push; then
|
||||
print_error "Failed to push changes to remote repository"
|
||||
print_warning "Changes have been committed locally but not pushed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_success "Git operations completed successfully!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to show usage
|
||||
show_usage() {
|
||||
echo "NOSTR Core Library Build Script"
|
||||
echo "==============================="
|
||||
echo ""
|
||||
echo "Usage: $0 [target] [-m \"commit message\"]"
|
||||
echo ""
|
||||
echo "Available targets:"
|
||||
echo " clean - Clean all build artifacts"
|
||||
echo " lib - Build static libraries for both x64 and ARM64 (default)"
|
||||
echo " x64 - Build x64 static library only"
|
||||
echo " arm64 - Build ARM64 static library only"
|
||||
echo " all - Build both architectures and examples"
|
||||
echo " examples - Build example programs"
|
||||
echo " test - Run tests"
|
||||
echo " install - Install library to system"
|
||||
echo " uninstall - Remove library from system"
|
||||
echo " help - Show this help message"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -m \"message\" - Git commit message (triggers automatic git add, commit, push after successful build)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 lib -m \"Add new proof-of-work parameters\""
|
||||
echo " $0 x64 -m \"Fix OpenSSL minimal build configuration\""
|
||||
echo " $0 lib # Build without git operations"
|
||||
echo ""
|
||||
echo "Library outputs (both self-contained with secp256k1):"
|
||||
echo " libnostr_core.a - x86_64 static library"
|
||||
echo " libnostr_core_arm64.a - ARM64 static library"
|
||||
echo " examples/* - Example programs"
|
||||
echo ""
|
||||
echo "Both libraries include secp256k1 objects internally."
|
||||
echo "Users only need to link with the library + -lm."
|
||||
}
|
||||
# Default values
|
||||
ARCHITECTURE=""
|
||||
FORCE_NIPS=""
|
||||
VERBOSE=false
|
||||
HELP=false
|
||||
BUILD_TESTS=false
|
||||
|
||||
# Parse command line arguments
|
||||
TARGET=""
|
||||
COMMIT_MESSAGE=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-m)
|
||||
COMMIT_MESSAGE="$2"
|
||||
shift 2
|
||||
x64|x86_64|amd64)
|
||||
ARCHITECTURE="x64"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
print_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
arm64|aarch64)
|
||||
ARCHITECTURE="arm64"
|
||||
shift
|
||||
;;
|
||||
--nips=*)
|
||||
FORCE_NIPS="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--verbose|-v)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
--tests)
|
||||
BUILD_TESTS=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
HELP=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
TARGET="$1"
|
||||
else
|
||||
print_error "Multiple targets specified: $TARGET and $1"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
print_error "Unknown argument: $1"
|
||||
HELP=true
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set default target if none specified
|
||||
TARGET=${TARGET:-lib}
|
||||
# Show help
|
||||
if [ "$HELP" = true ]; then
|
||||
echo "NOSTR Core Library - Customer Build Script"
|
||||
echo ""
|
||||
echo "Usage: $0 [architecture] [options]"
|
||||
echo ""
|
||||
echo "Architectures:"
|
||||
echo " x64, x86_64, amd64 Build for x86-64 architecture"
|
||||
echo " arm64, aarch64 Build for ARM64 architecture"
|
||||
echo " (default) Build for current architecture"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --nips=1,5,6,19 Force specific NIPs (comma-separated)"
|
||||
echo " --nips=all Include all available NIPs"
|
||||
echo " --tests Build all test programs in tests/ directory"
|
||||
echo " --verbose, -v Verbose output"
|
||||
echo " --help, -h Show this help"
|
||||
echo ""
|
||||
echo "Auto-Detection:"
|
||||
echo " Script automatically scans your .c files for #include statements"
|
||||
echo " like #include \"nip001.h\" and compiles only needed NIPs."
|
||||
echo ""
|
||||
echo "Available NIPs:"
|
||||
echo " 001 - Basic Protocol (event creation, signing)"
|
||||
echo " 005 - DNS-based identifiers"
|
||||
echo " 006 - Key derivation from mnemonic"
|
||||
echo " 011 - Relay information document"
|
||||
echo " 013 - Proof of Work"
|
||||
echo " 019 - Bech32 encoding (nsec/npub)"
|
||||
echo " 044 - Encryption"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Auto-detect NIPs, build for current arch"
|
||||
echo " $0 x64 # Auto-detect NIPs, build for x64"
|
||||
echo " $0 --nips=1,6,19 # Force NIPs 1,6,19 only"
|
||||
echo " $0 arm64 --nips=all # Build all NIPs for ARM64"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$TARGET" in
|
||||
clean)
|
||||
print_status "Cleaning build artifacts..."
|
||||
make clean
|
||||
print_success "Clean completed"
|
||||
;;
|
||||
print_info "NOSTR Core Library - Customer Build Script"
|
||||
print_info "Auto-detecting needed NIPs from your source code..."
|
||||
|
||||
lib|library)
|
||||
increment_version
|
||||
print_status "Building both x64 and ARM64 static libraries..."
|
||||
make clean
|
||||
make
|
||||
# Detect NIPs from source files
|
||||
NEEDED_NIPS=""
|
||||
if [ -n "$FORCE_NIPS" ]; then
|
||||
if [ "$FORCE_NIPS" = "all" ]; then
|
||||
NEEDED_NIPS="001 005 006 011 013 019 044"
|
||||
print_info "Forced: Building all available NIPs"
|
||||
else
|
||||
# Convert comma-separated list to space-separated with 3-digit format
|
||||
NEEDED_NIPS=$(echo "$FORCE_NIPS" | tr ',' ' ' | sed 's/\b\([0-9]\)\b/00\1/g' | sed 's/\b\([0-9][0-9]\)\b/0\1/g')
|
||||
print_info "Forced NIPs: $NEEDED_NIPS"
|
||||
fi
|
||||
else
|
||||
# Auto-detect from .c files in current directory
|
||||
if ls *.c >/dev/null 2>&1; then
|
||||
# Scan for nip*.h includes (with or without nostr_core/ prefix)
|
||||
DETECTED=$(grep -h '#include[[:space:]]*["\<]\(nostr_core/\)\?nip[0-9][0-9][0-9]\.h["\>]' *.c 2>/dev/null | \
|
||||
sed 's/.*nip0*\([0-9]*\)\.h.*/\1/' | \
|
||||
sort -u | \
|
||||
sed 's/\b\([0-9]\)\b/00\1/g' | sed 's/\b\([0-9][0-9]\)\b/0\1/g')
|
||||
|
||||
# Check both libraries were built
|
||||
SUCCESS=0
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE_X64=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE_X64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
# Check for nostr_core.h (includes everything)
|
||||
if grep -q '#include[[:space:]]*["\<]nostr_core\.h["\>]' *.c 2>/dev/null; then
|
||||
print_info "Found #include \"nostr_core.h\" - building all NIPs"
|
||||
NEEDED_NIPS="001 005 006 011 013 019 044"
|
||||
elif [ -n "$DETECTED" ]; then
|
||||
NEEDED_NIPS="$DETECTED"
|
||||
print_success "Auto-detected NIPs: $(echo $NEEDED_NIPS | tr ' ' ',')"
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
print_warning "No NIP includes detected in *.c files"
|
||||
print_info "Defaulting to basic NIPs: 001, 006, 019"
|
||||
NEEDED_NIPS="001 006 019"
|
||||
fi
|
||||
else
|
||||
print_warning "No .c files found in current directory"
|
||||
print_info "Defaulting to basic NIPs: 001, 006, 019"
|
||||
NEEDED_NIPS="001 006 019"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
fi
|
||||
# Ensure NIP-001 is always included (required for core functionality)
|
||||
if ! echo "$NEEDED_NIPS" | grep -q "001"; then
|
||||
NEEDED_NIPS="001 $NEEDED_NIPS"
|
||||
print_info "Added NIP-001 (required for core functionality)"
|
||||
fi
|
||||
|
||||
if [ $SUCCESS -eq 2 ]; then
|
||||
print_success "Both architectures built successfully!"
|
||||
ls -la libnostr_core*.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build all libraries"
|
||||
exit 1
|
||||
fi
|
||||
# Determine architecture
|
||||
if [ -z "$ARCHITECTURE" ]; then
|
||||
ARCH=$(uname -m)
|
||||
case $ARCH in
|
||||
x86_64|amd64)
|
||||
ARCHITECTURE="x64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
ARCHITECTURE="arm64"
|
||||
;;
|
||||
*)
|
||||
ARCHITECTURE="x64" # Default fallback
|
||||
print_warning "Unknown architecture '$ARCH', defaulting to x64"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
print_info "Target architecture: $ARCHITECTURE"
|
||||
|
||||
# Set compiler based on architecture
|
||||
case $ARCHITECTURE in
|
||||
x64)
|
||||
CC="gcc"
|
||||
ARCH_SUFFIX="x64"
|
||||
;;
|
||||
|
||||
x64|x64-only)
|
||||
increment_version
|
||||
print_status "Building x64 static library only..."
|
||||
make clean
|
||||
make x64
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
exit 1
|
||||
fi
|
||||
arm64)
|
||||
CC="aarch64-linux-gnu-gcc"
|
||||
ARCH_SUFFIX="arm64"
|
||||
;;
|
||||
|
||||
arm64|arm64-only)
|
||||
increment_version
|
||||
print_status "Building ARM64 static library only..."
|
||||
make clean
|
||||
make arm64
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core_arm64.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
shared)
|
||||
increment_version
|
||||
print_status "Building shared library..."
|
||||
make clean
|
||||
make libnostr_core.so
|
||||
if [ -f "libnostr_core.so" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core.so")
|
||||
print_success "Shared library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core.so
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build shared library"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
all)
|
||||
increment_version
|
||||
print_status "Building all libraries and examples..."
|
||||
make clean
|
||||
make all
|
||||
|
||||
# Check both libraries and examples were built
|
||||
SUCCESS=0
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE_X64=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE_X64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
fi
|
||||
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
fi
|
||||
|
||||
if [ $SUCCESS -eq 2 ]; then
|
||||
print_success "All libraries and examples built successfully!"
|
||||
ls -la libnostr_core*.a
|
||||
ls -la examples/
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build all components"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
examples)
|
||||
increment_version
|
||||
print_status "Building both libraries and examples..."
|
||||
make clean
|
||||
make
|
||||
make examples
|
||||
|
||||
# Verify libraries were built
|
||||
if [ -f "libnostr_core.a" ] && [ -f "libnostr_core_arm64.a" ]; then
|
||||
print_success "Both libraries and examples built successfully"
|
||||
ls -la libnostr_core*.a
|
||||
ls -la examples/
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build libraries for examples"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
test)
|
||||
print_status "Running tests..."
|
||||
make clean
|
||||
make
|
||||
if make test-crypto 2>/dev/null; then
|
||||
print_success "All tests passed"
|
||||
else
|
||||
print_warning "Running simple test instead..."
|
||||
make test
|
||||
print_success "Basic test completed"
|
||||
fi
|
||||
;;
|
||||
|
||||
tests)
|
||||
print_status "Running tests..."
|
||||
make clean
|
||||
make
|
||||
if make test-crypto 2>/dev/null; then
|
||||
print_success "All tests passed"
|
||||
else
|
||||
print_warning "Running simple test instead..."
|
||||
make test
|
||||
print_success "Basic test completed"
|
||||
fi
|
||||
;;
|
||||
|
||||
install)
|
||||
increment_version
|
||||
print_status "Installing library to system..."
|
||||
make clean
|
||||
make all
|
||||
sudo make install
|
||||
print_success "Library installed to /usr/local"
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
;;
|
||||
|
||||
uninstall)
|
||||
print_status "Uninstalling library from system..."
|
||||
sudo make uninstall
|
||||
print_success "Library uninstalled"
|
||||
;;
|
||||
|
||||
help|--help|-h)
|
||||
show_usage
|
||||
;;
|
||||
|
||||
*)
|
||||
print_error "Unknown target: $TARGET"
|
||||
echo ""
|
||||
show_usage
|
||||
print_error "Unsupported architecture: $ARCHITECTURE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if compiler exists
|
||||
if ! command -v $CC &> /dev/null; then
|
||||
print_error "Compiler $CC not found"
|
||||
if [ "$ARCHITECTURE" = "arm64" ]; then
|
||||
print_info "Install ARM64 cross-compiler: sudo apt install gcc-aarch64-linux-gnu"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build source file list - Core crypto dependencies
|
||||
SOURCES="nostr_core/crypto/nostr_crypto.c"
|
||||
SOURCES="$SOURCES nostr_core/crypto/nostr_secp256k1.c"
|
||||
SOURCES="$SOURCES nostr_core/crypto/nostr_aes.c"
|
||||
SOURCES="$SOURCES nostr_core/crypto/nostr_chacha20.c"
|
||||
SOURCES="$SOURCES cjson/cJSON.c"
|
||||
SOURCES="$SOURCES nostr_core/utils.c"
|
||||
|
||||
# Add secp256k1 library path based on architecture
|
||||
case $ARCHITECTURE in
|
||||
x64)
|
||||
SECP256K1_LIB="secp256k1/.libs/libsecp256k1.a"
|
||||
;;
|
||||
arm64)
|
||||
SECP256K1_LIB="secp256k1/.libs/libsecp256k1_arm64.a"
|
||||
;;
|
||||
esac
|
||||
NIP_DESCRIPTIONS=""
|
||||
|
||||
for nip in $NEEDED_NIPS; do
|
||||
NIP_FILE="nostr_core/nip${nip}.c"
|
||||
if [ -f "$NIP_FILE" ]; then
|
||||
SOURCES="$SOURCES $NIP_FILE"
|
||||
case $nip in
|
||||
001) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-001(Basic)" ;;
|
||||
005) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-005(DNS)" ;;
|
||||
006) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-006(Keys)" ;;
|
||||
011) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-011(Relay-Info)" ;;
|
||||
013) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-013(PoW)" ;;
|
||||
019) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-019(Bech32)" ;;
|
||||
044) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-044(Encrypt)" ;;
|
||||
esac
|
||||
else
|
||||
print_warning "NIP file not found: $NIP_FILE - skipping"
|
||||
fi
|
||||
done
|
||||
|
||||
# Build flags
|
||||
CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"
|
||||
CFLAGS="$CFLAGS -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING"
|
||||
INCLUDES="-I. -Inostr_core -Inostr_core/crypto -Icjson -Isecp256k1/include -Inostr_websocket"
|
||||
INCLUDES="$INCLUDES -I./openssl-install/include -I./curl-install/include"
|
||||
|
||||
# Static libraries
|
||||
STATIC_LIBS="./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a"
|
||||
STATIC_LIBS="$STATIC_LIBS ./curl-install/lib/libcurl.a"
|
||||
|
||||
# Output library name
|
||||
OUTPUT="libnostr_core_${ARCH_SUFFIX}.a"
|
||||
|
||||
print_info "Compiling with: $CC"
|
||||
print_info "Including:$NIP_DESCRIPTIONS"
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Sources: $SOURCES"
|
||||
print_info "Flags: $CFLAGS $INCLUDES"
|
||||
fi
|
||||
|
||||
# Compile each source file to object file
|
||||
OBJECTS=""
|
||||
for source in $SOURCES; do
|
||||
if [ -f "$source" ]; then
|
||||
obj_name=$(basename "$source" .c).${ARCH_SUFFIX}.o
|
||||
OBJECTS="$OBJECTS $obj_name"
|
||||
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Compiling: $source -> $obj_name"
|
||||
fi
|
||||
|
||||
$CC $CFLAGS $INCLUDES -c "$source" -o "$obj_name"
|
||||
if [ $? -ne 0 ]; then
|
||||
print_error "Failed to compile $source"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_error "Source file not found: $source"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Create self-contained static library (extract all objects first)
|
||||
print_info "Creating self-contained static library: $OUTPUT"
|
||||
|
||||
# Create temporary directories for extracting objects
|
||||
TMP_SECP256K1=".tmp_secp256k1_$$"
|
||||
TMP_OPENSSL=".tmp_openssl_$$"
|
||||
TMP_CURL=".tmp_curl_$$"
|
||||
|
||||
mkdir -p "$TMP_SECP256K1" "$TMP_OPENSSL" "$TMP_CURL"
|
||||
|
||||
# Extract secp256k1 objects
|
||||
if [ -f "$SECP256K1_LIB" ]; then
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Extracting secp256k1 objects..."
|
||||
fi
|
||||
(cd "$TMP_SECP256K1" && ar x "../$SECP256K1_LIB")
|
||||
fi
|
||||
|
||||
# Extract OpenSSL objects
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Extracting OpenSSL objects..."
|
||||
fi
|
||||
(cd "$TMP_OPENSSL" && ar x "../openssl-install/lib64/libssl.a")
|
||||
(cd "$TMP_OPENSSL" && ar x "../openssl-install/lib64/libcrypto.a")
|
||||
|
||||
# Extract curl objects
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Extracting curl objects..."
|
||||
fi
|
||||
(cd "$TMP_CURL" && ar x "../curl-install/lib/libcurl.a")
|
||||
|
||||
# Combine all objects into final library
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info "Combining all objects into self-contained library..."
|
||||
fi
|
||||
ar rcs "$OUTPUT" $OBJECTS "$TMP_SECP256K1"/*.o "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o 2>/dev/null
|
||||
|
||||
# Cleanup temporary directories
|
||||
rm -rf "$TMP_SECP256K1" "$TMP_OPENSSL" "$TMP_CURL"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Cleanup object files
|
||||
rm -f $OBJECTS
|
||||
|
||||
# Show library info
|
||||
size_kb=$(du -k "$OUTPUT" | cut -f1)
|
||||
print_success "Built $OUTPUT (${size_kb}KB) with:$NIP_DESCRIPTIONS"
|
||||
|
||||
# Build tests if requested
|
||||
if [ "$BUILD_TESTS" = true ]; then
|
||||
print_info "Scanning tests/ directory for test programs..."
|
||||
|
||||
if [ ! -d "tests" ]; then
|
||||
print_warning "tests/ directory not found - skipping test builds"
|
||||
else
|
||||
TEST_COUNT=0
|
||||
SUCCESS_COUNT=0
|
||||
|
||||
# Find all .c files in tests/ directory (not subdirectories)
|
||||
while IFS= read -r -d '' test_file; do
|
||||
TEST_COUNT=$((TEST_COUNT + 1))
|
||||
test_name=$(basename "$test_file" .c)
|
||||
test_exe="tests/$test_name"
|
||||
|
||||
print_info "Building test: $test_name"
|
||||
|
||||
# Determine linking requirements based on file content
|
||||
LINK_FLAGS="-lm -static"
|
||||
|
||||
# Always include secp256k1 library if available
|
||||
if [ -f "$SECP256K1_LIB" ]; then
|
||||
LINK_FLAGS="$SECP256K1_LIB $LINK_FLAGS"
|
||||
fi
|
||||
|
||||
# Check if test needs curl/SSL libraries (NIP-05/NIP-11)
|
||||
if grep -q -E 'nip005|nip011|curl/curl\.h' "$test_file" 2>/dev/null; then
|
||||
# Check if curl and SSL libraries are available
|
||||
if [ -f "./curl-install/lib/libcurl.a" ] && [ -f "./openssl-install/lib64/libssl.a" ]; then
|
||||
LINK_FLAGS="./curl-install/lib/libcurl.a ./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a -lz -ldl -lpthread $LINK_FLAGS"
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info " Using full static SSL/curl linking for $test_name"
|
||||
fi
|
||||
else
|
||||
print_warning " Test $test_name needs curl/SSL but libraries not found - using basic linking"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Compile the test
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_info " Command: $CC $CFLAGS $INCLUDES \"$test_file\" -o \"$test_exe\" ./$OUTPUT $LINK_FLAGS"
|
||||
fi
|
||||
|
||||
if $CC $CFLAGS $INCLUDES "$test_file" -o "$test_exe" "./$OUTPUT" $LINK_FLAGS 2>/dev/null; then
|
||||
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
print_success " Built: $test_exe"
|
||||
fi
|
||||
else
|
||||
print_error " Failed to build: $test_name"
|
||||
fi
|
||||
|
||||
done < <(find tests/ -maxdepth 1 -name "*.c" -type f -print0)
|
||||
|
||||
if [ $TEST_COUNT -eq 0 ]; then
|
||||
print_warning "No .c files found in tests/ directory"
|
||||
else
|
||||
print_success "Built $SUCCESS_COUNT/$TEST_COUNT test programs"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Usage in your project:"
|
||||
echo " gcc your_app.c $OUTPUT -lz -ldl -lpthread -o your_app"
|
||||
echo ""
|
||||
else
|
||||
print_error "Failed to create static library"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,394 @@
|
|||
#!/bin/bash
|
||||
|
||||
# NOSTR Core Library Build Script
|
||||
# Provides convenient build targets for the standalone library
|
||||
# Automatically increments patch version with each build
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to automatically increment version
|
||||
increment_version() {
|
||||
print_status "Incrementing version..."
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
print_warning "Not in a git repository - skipping version increment"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Get the highest version tag (not necessarily the most recent chronologically)
|
||||
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0")
|
||||
if [[ -z "$LATEST_TAG" ]]; then
|
||||
LATEST_TAG="v0.1.0"
|
||||
fi
|
||||
|
||||
# Extract version components (remove 'v' prefix if present)
|
||||
VERSION=${LATEST_TAG#v}
|
||||
|
||||
# Parse major.minor.patch
|
||||
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
MAJOR=${BASH_REMATCH[1]}
|
||||
MINOR=${BASH_REMATCH[2]}
|
||||
PATCH=${BASH_REMATCH[3]}
|
||||
else
|
||||
print_error "Invalid version format in tag: $LATEST_TAG"
|
||||
print_error "Expected format: v0.1.0"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Increment patch version
|
||||
NEW_PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
|
||||
|
||||
print_status "Current version: $LATEST_TAG"
|
||||
print_status "New version: $NEW_VERSION"
|
||||
|
||||
# Create new git tag
|
||||
if git tag "$NEW_VERSION" 2>/dev/null; then
|
||||
print_success "Created new version tag: $NEW_VERSION"
|
||||
else
|
||||
print_warning "Tag $NEW_VERSION already exists - using existing version"
|
||||
NEW_VERSION=$LATEST_TAG
|
||||
fi
|
||||
|
||||
# Update VERSION file for compatibility
|
||||
echo "${NEW_VERSION#v}" > VERSION
|
||||
print_success "Updated VERSION file to ${NEW_VERSION#v}"
|
||||
}
|
||||
|
||||
# Function to perform git operations after successful build
|
||||
perform_git_operations() {
|
||||
local commit_message="$1"
|
||||
|
||||
if [[ -z "$commit_message" ]]; then
|
||||
return 0 # No commit message provided, skip git operations
|
||||
fi
|
||||
|
||||
print_status "Performing git operations..."
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
print_warning "Not in a git repository - skipping git operations"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
print_warning "No changes to commit"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add all changes
|
||||
print_status "Adding changes to git..."
|
||||
if ! git add .; then
|
||||
print_error "Failed to add changes to git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Commit changes
|
||||
print_status "Committing changes with message: '$commit_message'"
|
||||
if ! git commit -m "$commit_message"; then
|
||||
print_error "Failed to commit changes"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Push changes
|
||||
print_status "Pushing changes to remote repository..."
|
||||
if ! git push; then
|
||||
print_error "Failed to push changes to remote repository"
|
||||
print_warning "Changes have been committed locally but not pushed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_success "Git operations completed successfully!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to show usage
|
||||
show_usage() {
|
||||
echo "NOSTR Core Library Build Script"
|
||||
echo "==============================="
|
||||
echo ""
|
||||
echo "Usage: $0 [target] [-m \"commit message\"]"
|
||||
echo ""
|
||||
echo "Available targets:"
|
||||
echo " clean - Clean all build artifacts"
|
||||
echo " lib - Build static libraries for both x64 and ARM64 (default)"
|
||||
echo " x64 - Build x64 static library only"
|
||||
echo " arm64 - Build ARM64 static library only"
|
||||
echo " all - Build both architectures and examples"
|
||||
echo " examples - Build example programs"
|
||||
echo " test - Run tests"
|
||||
echo " install - Install library to system"
|
||||
echo " uninstall - Remove library from system"
|
||||
echo " help - Show this help message"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -m \"message\" - Git commit message (triggers automatic git add, commit, push after successful build)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 lib -m \"Add new proof-of-work parameters\""
|
||||
echo " $0 x64 -m \"Fix OpenSSL minimal build configuration\""
|
||||
echo " $0 lib # Build without git operations"
|
||||
echo ""
|
||||
echo "Library outputs (both self-contained with secp256k1):"
|
||||
echo " libnostr_core.a - x86_64 static library"
|
||||
echo " libnostr_core_arm64.a - ARM64 static library"
|
||||
echo " examples/* - Example programs"
|
||||
echo ""
|
||||
echo "Both libraries include secp256k1 objects internally."
|
||||
echo "Users only need to link with the library + -lm."
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
TARGET=""
|
||||
COMMIT_MESSAGE=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-m)
|
||||
COMMIT_MESSAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
print_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
TARGET="$1"
|
||||
else
|
||||
print_error "Multiple targets specified: $TARGET and $1"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set default target if none specified
|
||||
TARGET=${TARGET:-lib}
|
||||
|
||||
case "$TARGET" in
|
||||
clean)
|
||||
print_status "Cleaning build artifacts..."
|
||||
make clean
|
||||
print_success "Clean completed"
|
||||
;;
|
||||
|
||||
lib|library)
|
||||
increment_version
|
||||
print_status "Building both x64 and ARM64 static libraries..."
|
||||
make clean
|
||||
make
|
||||
|
||||
# Check both libraries were built
|
||||
SUCCESS=0
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE_X64=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE_X64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
fi
|
||||
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
fi
|
||||
|
||||
if [ $SUCCESS -eq 2 ]; then
|
||||
print_success "Both architectures built successfully!"
|
||||
ls -la libnostr_core*.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build all libraries"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
x64|x64-only)
|
||||
increment_version
|
||||
print_status "Building x64 static library only..."
|
||||
make clean
|
||||
make x64
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
arm64|arm64-only)
|
||||
increment_version
|
||||
print_status "Building ARM64 static library only..."
|
||||
make clean
|
||||
make arm64
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core_arm64.a
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
shared)
|
||||
increment_version
|
||||
print_status "Building shared library..."
|
||||
make clean
|
||||
make libnostr_core.so
|
||||
if [ -f "libnostr_core.so" ]; then
|
||||
SIZE=$(stat -c%s "libnostr_core.so")
|
||||
print_success "Shared library built successfully (${SIZE} bytes)"
|
||||
ls -la libnostr_core.so
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build shared library"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
all)
|
||||
increment_version
|
||||
print_status "Building all libraries and examples..."
|
||||
make clean
|
||||
make all
|
||||
|
||||
# Check both libraries and examples were built
|
||||
SUCCESS=0
|
||||
if [ -f "libnostr_core.a" ]; then
|
||||
SIZE_X64=$(stat -c%s "libnostr_core.a")
|
||||
print_success "x64 static library built successfully (${SIZE_X64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build x64 static library"
|
||||
fi
|
||||
|
||||
if [ -f "libnostr_core_arm64.a" ]; then
|
||||
SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a")
|
||||
print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
print_error "Failed to build ARM64 static library"
|
||||
fi
|
||||
|
||||
if [ $SUCCESS -eq 2 ]; then
|
||||
print_success "All libraries and examples built successfully!"
|
||||
ls -la libnostr_core*.a
|
||||
ls -la examples/
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build all components"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
examples)
|
||||
increment_version
|
||||
print_status "Building both libraries and examples..."
|
||||
make clean
|
||||
make
|
||||
make examples
|
||||
|
||||
# Verify libraries were built
|
||||
if [ -f "libnostr_core.a" ] && [ -f "libnostr_core_arm64.a" ]; then
|
||||
print_success "Both libraries and examples built successfully"
|
||||
ls -la libnostr_core*.a
|
||||
ls -la examples/
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
else
|
||||
print_error "Failed to build libraries for examples"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
test)
|
||||
print_status "Running tests..."
|
||||
make clean
|
||||
make
|
||||
if make test-crypto 2>/dev/null; then
|
||||
print_success "All tests passed"
|
||||
else
|
||||
print_warning "Running simple test instead..."
|
||||
make test
|
||||
print_success "Basic test completed"
|
||||
fi
|
||||
;;
|
||||
|
||||
tests)
|
||||
print_status "Running tests..."
|
||||
make clean
|
||||
make
|
||||
if make test-crypto 2>/dev/null; then
|
||||
print_success "All tests passed"
|
||||
else
|
||||
print_warning "Running simple test instead..."
|
||||
make test
|
||||
print_success "Basic test completed"
|
||||
fi
|
||||
;;
|
||||
|
||||
install)
|
||||
increment_version
|
||||
print_status "Installing library to system..."
|
||||
make clean
|
||||
make all
|
||||
sudo make install
|
||||
print_success "Library installed to /usr/local"
|
||||
perform_git_operations "$COMMIT_MESSAGE"
|
||||
;;
|
||||
|
||||
uninstall)
|
||||
print_status "Uninstalling library from system..."
|
||||
sudo make uninstall
|
||||
print_success "Library uninstalled"
|
||||
;;
|
||||
|
||||
help|--help|-h)
|
||||
show_usage
|
||||
;;
|
||||
|
||||
*)
|
||||
print_error "Unknown target: $TARGET"
|
||||
echo ""
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Example: Basic NOSTR functionality
|
||||
* This example shows auto-detection working with selective includes
|
||||
*/
|
||||
|
||||
#include "nostr_core/nip001.h" // Basic protocol
|
||||
#include "nostr_core/nip006.h" // Key generation (will be created next)
|
||||
#include "nostr_core/nip019.h" // Bech32 encoding (will be created next)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("NOSTR Core Library - Basic Example\n");
|
||||
|
||||
// Initialize library
|
||||
if (nostr_init() != 0) {
|
||||
printf("Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Library initialized successfully!\n");
|
||||
|
||||
// Generate keypair (from NIP-006)
|
||||
// unsigned char private_key[32], public_key[32];
|
||||
// nostr_generate_keypair(private_key, public_key);
|
||||
|
||||
// Convert to bech32 (from NIP-019)
|
||||
// char nsec[100];
|
||||
// nostr_key_to_bech32(private_key, "nsec", nsec);
|
||||
|
||||
// Create basic event (from NIP-001)
|
||||
// cJSON* event = nostr_create_and_sign_event(1, "Hello Nostr!", NULL, private_key, 0);
|
||||
|
||||
printf("Example completed - the build script should detect NIPs: 001, 006, 019\n");
|
||||
|
||||
// Cleanup
|
||||
nostr_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Example demonstrating modular NIP usage
|
||||
* Shows how different NIPs can be used independently
|
||||
*/
|
||||
|
||||
#include "nostr_core/nip001.h" // Basic protocol
|
||||
#include "nostr_core/nip005.h" // NIP-05 DNS verification
|
||||
#include "nostr_core/nip006.h" // Key derivation
|
||||
#include "nostr_core/nip011.h" // Relay information
|
||||
#include "nostr_core/nip013.h" // Proof of work
|
||||
#include "nostr_core/nip019.h" // Bech32 encoding
|
||||
#include "nostr_core/utils.h" // Utility functions
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
printf("=== Modular NOSTR Core Library Demo ===\n\n");
|
||||
|
||||
// Initialize the library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test NIP-006: Key generation and detection
|
||||
printf("1. NIP-006: Key Generation and Input Detection\n");
|
||||
unsigned char private_key[32], public_key[32];
|
||||
|
||||
if (nostr_generate_keypair(private_key, public_key) == NOSTR_SUCCESS) {
|
||||
char private_hex[65], public_hex[65];
|
||||
nostr_bytes_to_hex(private_key, 32, private_hex);
|
||||
nostr_bytes_to_hex(public_key, 32, public_hex);
|
||||
|
||||
printf(" Generated keypair:\n");
|
||||
printf(" Private: %s\n", private_hex);
|
||||
printf(" Public: %s\n", public_hex);
|
||||
|
||||
// Test input type detection
|
||||
nostr_input_type_t type = nostr_detect_input_type(private_hex);
|
||||
printf(" Input type: %s\n",
|
||||
type == NOSTR_INPUT_NSEC_HEX ? "NSEC_HEX" :
|
||||
type == NOSTR_INPUT_NSEC_BECH32 ? "NSEC_BECH32" :
|
||||
type == NOSTR_INPUT_MNEMONIC ? "MNEMONIC" : "UNKNOWN");
|
||||
}
|
||||
|
||||
// Test NIP-019: Bech32 encoding
|
||||
printf("\n2. NIP-019: Bech32 Encoding\n");
|
||||
char bech32_nsec[200], bech32_npub[200];
|
||||
|
||||
if (nostr_key_to_bech32(private_key, "nsec", bech32_nsec) == NOSTR_SUCCESS) {
|
||||
printf(" nsec: %s\n", bech32_nsec);
|
||||
}
|
||||
|
||||
if (nostr_key_to_bech32(public_key, "npub", bech32_npub) == NOSTR_SUCCESS) {
|
||||
printf(" npub: %s\n", bech32_npub);
|
||||
}
|
||||
|
||||
// Test NIP-001: Event creation
|
||||
printf("\n3. NIP-001: Event Creation\n");
|
||||
cJSON* event = nostr_create_and_sign_event(1, "Hello from modular NOSTR!", NULL, private_key, 0);
|
||||
if (event) {
|
||||
char* event_json = cJSON_Print(event);
|
||||
printf(" Created event: %s\n", event_json);
|
||||
free(event_json);
|
||||
cJSON_Delete(event);
|
||||
}
|
||||
|
||||
// Test NIP-013: Proof of Work (light test)
|
||||
printf("\n4. NIP-013: Proof of Work\n");
|
||||
cJSON* pow_event = nostr_create_and_sign_event(1, "PoW test message", NULL, private_key, 0);
|
||||
if (pow_event) {
|
||||
printf(" Adding PoW (target difficulty: 4)...\n");
|
||||
int result = nostr_add_proof_of_work(pow_event, private_key, 4, 100000, 10000, 5000, NULL, NULL);
|
||||
if (result == NOSTR_SUCCESS) {
|
||||
cJSON* id_item = cJSON_GetObjectItem(pow_event, "id");
|
||||
if (id_item) {
|
||||
printf(" PoW success! Event ID: %s\n", cJSON_GetStringValue(id_item));
|
||||
}
|
||||
} else {
|
||||
printf(" PoW failed or timeout\n");
|
||||
}
|
||||
cJSON_Delete(pow_event);
|
||||
}
|
||||
|
||||
// Test NIP-005: DNS verification (parse only - no network call in example)
|
||||
printf("\n5. NIP-005: DNS Identifier Parsing\n");
|
||||
const char* test_json = "{\"names\":{\"test\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"}}";
|
||||
char found_pubkey[65];
|
||||
|
||||
int result = nostr_nip05_parse_well_known(test_json, "test", found_pubkey, NULL, NULL);
|
||||
if (result == NOSTR_SUCCESS) {
|
||||
printf(" Parsed pubkey from test JSON: %s\n", found_pubkey);
|
||||
}
|
||||
|
||||
// Test NIP-011: Just show the structure exists
|
||||
printf("\n6. NIP-011: Relay Information Structure\n");
|
||||
printf(" Relay info structure available for fetching relay metadata\n");
|
||||
printf(" (Network calls not performed in this example)\n");
|
||||
|
||||
printf("\n=== All modular NIPs working! ===\n");
|
||||
|
||||
nostr_cleanup();
|
||||
return 0;
|
||||
}
|
BIN
libnostr_core.a
BIN
libnostr_core.a
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
#include "nostr_crypto.h"
|
||||
#include "nostr_core.h" // For error constants
|
||||
#include "../nostr_common.h" // For error constants
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-001: Basic Protocol Flow
|
||||
*
|
||||
* Event creation, signing, serialization and core protocol functions
|
||||
*/
|
||||
|
||||
#include "nip001.h"
|
||||
#include "nostr_crypto.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
// Declare utility functions
|
||||
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
|
||||
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
|
||||
|
||||
/**
|
||||
* Initialize the NOSTR library
|
||||
*/
|
||||
int nostr_init(void) {
|
||||
if (nostr_crypto_init() != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the NOSTR library
|
||||
*/
|
||||
void nostr_cleanup(void) {
|
||||
nostr_crypto_cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert error code to human-readable string
|
||||
*/
|
||||
const char* nostr_strerror(int error_code) {
|
||||
switch (error_code) {
|
||||
case NOSTR_SUCCESS: return "Success";
|
||||
case NOSTR_ERROR_INVALID_INPUT: return "Invalid input";
|
||||
case NOSTR_ERROR_CRYPTO_FAILED: return "Cryptographic operation failed";
|
||||
case NOSTR_ERROR_MEMORY_FAILED: return "Memory allocation failed";
|
||||
case NOSTR_ERROR_IO_FAILED: return "I/O operation failed";
|
||||
case NOSTR_ERROR_NETWORK_FAILED: return "Network operation failed";
|
||||
case NOSTR_ERROR_NIP04_INVALID_FORMAT: return "NIP-04 invalid format";
|
||||
case NOSTR_ERROR_NIP04_DECRYPT_FAILED: return "NIP-04 decryption failed";
|
||||
case NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL: return "NIP-04 buffer too small";
|
||||
case NOSTR_ERROR_NIP05_INVALID_IDENTIFIER: return "NIP-05: Invalid identifier format";
|
||||
case NOSTR_ERROR_NIP05_HTTP_FAILED: return "NIP-05: HTTP request failed";
|
||||
case NOSTR_ERROR_NIP05_JSON_PARSE_FAILED: return "NIP-05: JSON parsing failed";
|
||||
case NOSTR_ERROR_NIP05_NAME_NOT_FOUND: return "NIP-05: Name not found in .well-known";
|
||||
case NOSTR_ERROR_NIP05_PUBKEY_MISMATCH: return "NIP-05: Public key mismatch";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and sign a NOSTR event
|
||||
*/
|
||||
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp) {
|
||||
if (!private_key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
content = ""; // Default to empty content
|
||||
}
|
||||
|
||||
// Convert private key to public key
|
||||
unsigned char public_key[32];
|
||||
if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert public key to hex
|
||||
char pubkey_hex[65];
|
||||
nostr_bytes_to_hex(public_key, 32, pubkey_hex);
|
||||
|
||||
// Create event structure
|
||||
cJSON* event = cJSON_CreateObject();
|
||||
if (!event) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Use provided timestamp or current time if timestamp is 0
|
||||
time_t event_time = (timestamp == 0) ? time(NULL) : timestamp;
|
||||
|
||||
cJSON_AddStringToObject(event, "pubkey", pubkey_hex);
|
||||
cJSON_AddNumberToObject(event, "created_at", (double)event_time);
|
||||
cJSON_AddNumberToObject(event, "kind", kind);
|
||||
|
||||
// Add tags (copy provided tags or create empty array)
|
||||
if (tags) {
|
||||
cJSON_AddItemToObject(event, "tags", cJSON_Duplicate(tags, 1));
|
||||
} else {
|
||||
cJSON_AddItemToObject(event, "tags", cJSON_CreateArray());
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(event, "content", content);
|
||||
|
||||
// ============================================================================
|
||||
// INLINE SERIALIZATION AND SIGNING LOGIC
|
||||
// ============================================================================
|
||||
|
||||
// Get event fields for serialization
|
||||
cJSON* pubkey_item = cJSON_GetObjectItem(event, "pubkey");
|
||||
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
|
||||
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
|
||||
cJSON* content_item = cJSON_GetObjectItem(event, "content");
|
||||
|
||||
if (!pubkey_item || !created_at_item || !kind_item || !tags_item || !content_item) {
|
||||
cJSON_Delete(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create serialization array: [0, pubkey, created_at, kind, tags, content]
|
||||
cJSON* serialize_array = cJSON_CreateArray();
|
||||
if (!serialize_array) {
|
||||
cJSON_Delete(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_CreateNumber(0));
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(pubkey_item, 1));
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(created_at_item, 1));
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(kind_item, 1));
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(tags_item, 1));
|
||||
cJSON_AddItemToArray(serialize_array, cJSON_Duplicate(content_item, 1));
|
||||
|
||||
char* serialize_string = cJSON_PrintUnformatted(serialize_array);
|
||||
cJSON_Delete(serialize_array);
|
||||
|
||||
if (!serialize_string) {
|
||||
cJSON_Delete(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Hash the serialized event
|
||||
unsigned char event_hash[32];
|
||||
if (nostr_sha256((const unsigned char*)serialize_string, strlen(serialize_string), event_hash) != 0) {
|
||||
free(serialize_string);
|
||||
cJSON_Delete(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert hash to hex for event ID
|
||||
char event_id[65];
|
||||
nostr_bytes_to_hex(event_hash, 32, event_id);
|
||||
|
||||
// Sign the hash using ECDSA
|
||||
unsigned char signature[64];
|
||||
if (nostr_ec_sign(private_key, event_hash, signature) != 0) {
|
||||
free(serialize_string);
|
||||
cJSON_Delete(event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert signature to hex
|
||||
char sig_hex[129];
|
||||
nostr_bytes_to_hex(signature, 64, sig_hex);
|
||||
|
||||
// Add ID and signature to the event
|
||||
cJSON_AddStringToObject(event, "id", event_id);
|
||||
cJSON_AddStringToObject(event, "sig", sig_hex);
|
||||
|
||||
free(serialize_string);
|
||||
|
||||
return event;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-001: Basic Protocol Flow
|
||||
*
|
||||
* Event creation, signing, serialization and core protocol functions
|
||||
*/
|
||||
|
||||
#ifndef NIP001_H
|
||||
#define NIP001_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include "../cjson/cJSON.h"
|
||||
|
||||
// Error codes
|
||||
#define NOSTR_SUCCESS 0
|
||||
#define NOSTR_ERROR_INVALID_INPUT -1
|
||||
#define NOSTR_ERROR_CRYPTO_FAILED -2
|
||||
#define NOSTR_ERROR_MEMORY_FAILED -3
|
||||
#define NOSTR_ERROR_IO_FAILED -4
|
||||
#define NOSTR_ERROR_NETWORK_FAILED -5
|
||||
#define NOSTR_ERROR_NIP04_INVALID_FORMAT -10
|
||||
#define NOSTR_ERROR_NIP04_DECRYPT_FAILED -11
|
||||
#define NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL -12
|
||||
#define NOSTR_ERROR_NIP05_INVALID_IDENTIFIER -20
|
||||
#define NOSTR_ERROR_NIP05_HTTP_FAILED -21
|
||||
#define NOSTR_ERROR_NIP05_JSON_PARSE_FAILED -22
|
||||
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -23
|
||||
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -24
|
||||
|
||||
// Function declarations
|
||||
int nostr_init(void);
|
||||
void nostr_cleanup(void);
|
||||
const char* nostr_strerror(int error_code);
|
||||
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp);
|
||||
|
||||
#endif // NIP001_H
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-005: Mapping Nostr keys to DNS-based internet identifiers
|
||||
*/
|
||||
|
||||
#include "nip005.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> // For strcasecmp
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef DISABLE_NIP05
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
// Maximum sizes for NIP-05 operations
|
||||
#define NIP05_MAX_URL_SIZE 512
|
||||
#define NIP05_MAX_RESPONSE_SIZE 8192
|
||||
#define NIP05_MAX_IDENTIFIER_SIZE 256
|
||||
#define NIP05_DEFAULT_TIMEOUT 10
|
||||
|
||||
#ifndef DISABLE_NIP05
|
||||
|
||||
// Structure for HTTP response handling
|
||||
typedef struct {
|
||||
char* data;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} nip05_http_response_t;
|
||||
|
||||
/**
|
||||
* Callback function for curl to write HTTP response data
|
||||
*/
|
||||
static size_t nip05_write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||
nip05_http_response_t* response = (nip05_http_response_t*)userp;
|
||||
size_t total_size = size * nmemb;
|
||||
|
||||
// Check if we need to expand the buffer
|
||||
if (response->size + total_size >= response->capacity) {
|
||||
size_t new_capacity = response->capacity * 2;
|
||||
if (new_capacity < response->size + total_size + 1) {
|
||||
new_capacity = response->size + total_size + 1;
|
||||
}
|
||||
|
||||
char* new_data = realloc(response->data, new_capacity);
|
||||
if (!new_data) {
|
||||
return 0; // Out of memory
|
||||
}
|
||||
|
||||
response->data = new_data;
|
||||
response->capacity = new_capacity;
|
||||
}
|
||||
|
||||
// Append the new data
|
||||
memcpy(response->data + response->size, contents, total_size);
|
||||
response->size += total_size;
|
||||
response->data[response->size] = '\0';
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and validate a NIP-05 identifier into local part and domain
|
||||
*/
|
||||
static int nip05_parse_identifier(const char* identifier, char* local_part, char* domain) {
|
||||
if (!identifier || !local_part || !domain) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Find the @ symbol
|
||||
const char* at_pos = strchr(identifier, '@');
|
||||
if (!at_pos) {
|
||||
return NOSTR_ERROR_NIP05_INVALID_IDENTIFIER;
|
||||
}
|
||||
|
||||
// Extract local part
|
||||
size_t local_len = at_pos - identifier;
|
||||
if (local_len == 0 || local_len >= 64) {
|
||||
return NOSTR_ERROR_NIP05_INVALID_IDENTIFIER;
|
||||
}
|
||||
|
||||
strncpy(local_part, identifier, local_len);
|
||||
local_part[local_len] = '\0';
|
||||
|
||||
// Extract domain
|
||||
const char* domain_start = at_pos + 1;
|
||||
size_t domain_len = strlen(domain_start);
|
||||
if (domain_len == 0 || domain_len >= 256) {
|
||||
return NOSTR_ERROR_NIP05_INVALID_IDENTIFIER;
|
||||
}
|
||||
|
||||
strcpy(domain, domain_start);
|
||||
|
||||
// Validate characters in local part (a-z0-9-_.)
|
||||
for (size_t i = 0; i < local_len; i++) {
|
||||
char c = local_part[i];
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.')) {
|
||||
return NOSTR_ERROR_NIP05_INVALID_IDENTIFIER;
|
||||
}
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make HTTP GET request to a URL
|
||||
*/
|
||||
static int nip05_http_get(const char* url, int timeout_seconds, char** response_data) {
|
||||
if (!url || !response_data) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
nip05_http_response_t response = {0};
|
||||
response.capacity = 1024;
|
||||
response.data = malloc(response.capacity);
|
||||
if (!response.data) {
|
||||
curl_easy_cleanup(curl);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
response.data[0] = '\0';
|
||||
|
||||
// Set curl options with proper type casting to fix warnings
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)nip05_write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)(timeout_seconds > 0 ? timeout_seconds : NIP05_DEFAULT_TIMEOUT));
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); // NIP-05 forbids redirects
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "nostr-core/1.0");
|
||||
|
||||
// Perform the request
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
long response_code = 0;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
free(response.data);
|
||||
return NOSTR_ERROR_NIP05_HTTP_FAILED;
|
||||
}
|
||||
|
||||
if (response_code != 200) {
|
||||
free(response.data);
|
||||
return NOSTR_ERROR_NIP05_HTTP_FAILED;
|
||||
}
|
||||
|
||||
*response_data = response.data;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a hex string is a valid public key
|
||||
*/
|
||||
static int nip05_validate_pubkey_hex(const char* hex_pubkey) {
|
||||
if (!hex_pubkey) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
size_t len = strlen(hex_pubkey);
|
||||
if (len != 64) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Check all characters are valid hex
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char c = hex_pubkey[i];
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a .well-known/nostr.json response and extract pubkey and relays for a specific name
|
||||
*/
|
||||
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
|
||||
char* pubkey_hex_out, char*** relays, int* relay_count) {
|
||||
if (!json_response || !local_part) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Initialize outputs
|
||||
if (pubkey_hex_out) {
|
||||
pubkey_hex_out[0] = '\0';
|
||||
}
|
||||
if (relays) {
|
||||
*relays = NULL;
|
||||
}
|
||||
if (relay_count) {
|
||||
*relay_count = 0;
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
cJSON* json = cJSON_Parse(json_response);
|
||||
if (!json) {
|
||||
return NOSTR_ERROR_NIP05_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
int result = NOSTR_ERROR_NIP05_NAME_NOT_FOUND;
|
||||
|
||||
// Get the "names" object
|
||||
cJSON* names = cJSON_GetObjectItem(json, "names");
|
||||
if (names && cJSON_IsObject(names)) {
|
||||
cJSON* pubkey_item = cJSON_GetObjectItem(names, local_part);
|
||||
if (pubkey_item && cJSON_IsString(pubkey_item)) {
|
||||
const char* found_pubkey = cJSON_GetStringValue(pubkey_item);
|
||||
|
||||
// Validate the public key format
|
||||
if (nip05_validate_pubkey_hex(found_pubkey) == NOSTR_SUCCESS) {
|
||||
if (pubkey_hex_out) {
|
||||
strcpy(pubkey_hex_out, found_pubkey);
|
||||
}
|
||||
result = NOSTR_SUCCESS;
|
||||
|
||||
// Extract relays if requested
|
||||
if (relays && relay_count) {
|
||||
cJSON* relays_obj = cJSON_GetObjectItem(json, "relays");
|
||||
if (relays_obj && cJSON_IsObject(relays_obj)) {
|
||||
cJSON* user_relays = cJSON_GetObjectItem(relays_obj, found_pubkey);
|
||||
if (user_relays && cJSON_IsArray(user_relays)) {
|
||||
int relay_array_size = cJSON_GetArraySize(user_relays);
|
||||
if (relay_array_size > 0) {
|
||||
char** relay_array = malloc(relay_array_size * sizeof(char*));
|
||||
if (relay_array) {
|
||||
int valid_relays = 0;
|
||||
for (int i = 0; i < relay_array_size; i++) {
|
||||
cJSON* relay_item = cJSON_GetArrayItem(user_relays, i);
|
||||
if (relay_item && cJSON_IsString(relay_item)) {
|
||||
const char* relay_url = cJSON_GetStringValue(relay_item);
|
||||
if (relay_url && strlen(relay_url) > 0) {
|
||||
relay_array[valid_relays] = malloc(strlen(relay_url) + 1);
|
||||
if (relay_array[valid_relays]) {
|
||||
strcpy(relay_array[valid_relays], relay_url);
|
||||
valid_relays++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_relays > 0) {
|
||||
*relays = relay_array;
|
||||
*relay_count = valid_relays;
|
||||
} else {
|
||||
free(relay_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a public key from a NIP-05 identifier
|
||||
*/
|
||||
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
|
||||
char*** relays, int* relay_count, int timeout_seconds) {
|
||||
if (!nip05_identifier) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
char local_part[64];
|
||||
char domain[256];
|
||||
char url[NIP05_MAX_URL_SIZE];
|
||||
|
||||
// Parse the identifier
|
||||
int parse_result = nip05_parse_identifier(nip05_identifier, local_part, domain);
|
||||
if (parse_result != NOSTR_SUCCESS) {
|
||||
return parse_result;
|
||||
}
|
||||
|
||||
// Construct the .well-known URL
|
||||
int url_result = snprintf(url, sizeof(url), "https://%s/.well-known/nostr.json?name=%s",
|
||||
domain, local_part);
|
||||
if (url_result >= (int)sizeof(url) || url_result < 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Make HTTP request
|
||||
char* response_data = NULL;
|
||||
int http_result = nip05_http_get(url, timeout_seconds, &response_data);
|
||||
if (http_result != NOSTR_SUCCESS) {
|
||||
return http_result;
|
||||
}
|
||||
|
||||
// Parse the response
|
||||
int parse_response_result = nostr_nip05_parse_well_known(response_data, local_part,
|
||||
pubkey_hex_out, relays, relay_count);
|
||||
|
||||
free(response_data);
|
||||
return parse_response_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a NIP-05 identifier against a public key
|
||||
*/
|
||||
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
|
||||
char*** relays, int* relay_count, int timeout_seconds) {
|
||||
if (!nip05_identifier || !pubkey_hex) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Validate the input public key format
|
||||
if (nip05_validate_pubkey_hex(pubkey_hex) != NOSTR_SUCCESS) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
char found_pubkey[65];
|
||||
|
||||
// Lookup the public key for this identifier
|
||||
int lookup_result = nostr_nip05_lookup(nip05_identifier, found_pubkey,
|
||||
relays, relay_count, timeout_seconds);
|
||||
if (lookup_result != NOSTR_SUCCESS) {
|
||||
return lookup_result;
|
||||
}
|
||||
|
||||
// Compare the public keys (case insensitive)
|
||||
if (strcasecmp(pubkey_hex, found_pubkey) != 0) {
|
||||
// Clean up relays if verification failed
|
||||
if (relays && *relays) {
|
||||
for (int i = 0; i < (relay_count ? *relay_count : 0); i++) {
|
||||
free((*relays)[i]);
|
||||
}
|
||||
free(*relays);
|
||||
*relays = NULL;
|
||||
}
|
||||
if (relay_count) {
|
||||
*relay_count = 0;
|
||||
}
|
||||
return NOSTR_ERROR_NIP05_PUBKEY_MISMATCH;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
#else // DISABLE_NIP05
|
||||
|
||||
/**
|
||||
* Stub implementations when NIP-05 is disabled at compile time
|
||||
*/
|
||||
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
|
||||
char* pubkey_hex_out, char*** relays, int* relay_count) {
|
||||
(void)json_response;
|
||||
(void)local_part;
|
||||
(void)pubkey_hex_out;
|
||||
(void)relays;
|
||||
(void)relay_count;
|
||||
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
|
||||
}
|
||||
|
||||
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
|
||||
char*** relays, int* relay_count, int timeout_seconds) {
|
||||
(void)nip05_identifier;
|
||||
(void)pubkey_hex_out;
|
||||
(void)relays;
|
||||
(void)relay_count;
|
||||
(void)timeout_seconds;
|
||||
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
|
||||
}
|
||||
|
||||
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
|
||||
char*** relays, int* relay_count, int timeout_seconds) {
|
||||
(void)nip05_identifier;
|
||||
(void)pubkey_hex;
|
||||
(void)relays;
|
||||
(void)relay_count;
|
||||
(void)timeout_seconds;
|
||||
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
|
||||
}
|
||||
|
||||
#endif // DISABLE_NIP05
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-005: Mapping Nostr keys to DNS-based internet identifiers
|
||||
*/
|
||||
|
||||
#ifndef NIP005_H
|
||||
#define NIP005_H
|
||||
|
||||
#include "nip001.h"
|
||||
|
||||
// Function declarations
|
||||
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
|
||||
char* pubkey_hex_out, char*** relays, int* relay_count);
|
||||
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
|
||||
char*** relays, int* relay_count, int timeout_seconds);
|
||||
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
|
||||
char*** relays, int* relay_count, int timeout_seconds);
|
||||
|
||||
#endif // NIP005_H
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-006: Key Derivation from Mnemonic
|
||||
*/
|
||||
|
||||
#include "nip006.h"
|
||||
#include "utils.h"
|
||||
#include "nostr_crypto.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key) {
|
||||
if (!private_key || !public_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Generate random entropy using /dev/urandom
|
||||
FILE* urandom = fopen("/dev/urandom", "rb");
|
||||
if (!urandom) {
|
||||
return NOSTR_ERROR_IO_FAILED;
|
||||
}
|
||||
|
||||
if (fread(private_key, 1, 32, urandom) != 32) {
|
||||
fclose(urandom);
|
||||
return NOSTR_ERROR_IO_FAILED;
|
||||
}
|
||||
fclose(urandom);
|
||||
|
||||
// Validate private key
|
||||
if (nostr_ec_private_key_verify(private_key) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Generate public key from private key (already x-only for NOSTR)
|
||||
if (nostr_ec_public_key_from_private_key(private_key, public_key) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
|
||||
int account, unsigned char* private_key,
|
||||
unsigned char* public_key) {
|
||||
if (!mnemonic || mnemonic_size < 256 || !private_key || !public_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Generate entropy for 12-word mnemonic
|
||||
unsigned char entropy[16];
|
||||
FILE* urandom = fopen("/dev/urandom", "rb");
|
||||
if (!urandom) {
|
||||
return NOSTR_ERROR_IO_FAILED;
|
||||
}
|
||||
|
||||
if (fread(entropy, 1, sizeof(entropy), urandom) != sizeof(entropy)) {
|
||||
fclose(urandom);
|
||||
return NOSTR_ERROR_IO_FAILED;
|
||||
}
|
||||
fclose(urandom);
|
||||
|
||||
// Generate mnemonic from entropy
|
||||
if (nostr_bip39_mnemonic_from_bytes(entropy, sizeof(entropy), mnemonic) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Derive keys from the generated mnemonic
|
||||
return nostr_derive_keys_from_mnemonic(mnemonic, account, private_key, public_key);
|
||||
}
|
||||
|
||||
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
|
||||
unsigned char* private_key, unsigned char* public_key) {
|
||||
if (!mnemonic || !private_key || !public_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Validate mnemonic
|
||||
if (nostr_bip39_mnemonic_validate(mnemonic) != 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Convert mnemonic to seed
|
||||
unsigned char seed[64];
|
||||
if (nostr_bip39_mnemonic_to_seed(mnemonic, "", seed, sizeof(seed)) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Derive master key from seed
|
||||
nostr_hd_key_t master_key;
|
||||
if (nostr_bip32_key_from_seed(seed, sizeof(seed), &master_key) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// NIP-06 path: m/44'/1237'/account'/0/0
|
||||
nostr_hd_key_t derived_key;
|
||||
uint32_t path[] = {
|
||||
0x80000000 + 44, // 44' (hardened)
|
||||
0x80000000 + 1237, // 1237' (hardened)
|
||||
0x80000000 + account, // account' (hardened)
|
||||
0, // 0 (not hardened)
|
||||
0 // 0 (not hardened)
|
||||
};
|
||||
|
||||
if (nostr_bip32_derive_path(&master_key, path, sizeof(path) / sizeof(path[0]), &derived_key) != 0) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Extract private key and public key
|
||||
memcpy(private_key, derived_key.private_key, 32);
|
||||
memcpy(public_key, derived_key.public_key + 1, 32); // Remove compression prefix for x-only
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
// Note: nostr_detect_input_type, nostr_decode_nsec, and nostr_decode_npub
|
||||
// are implemented in NIP-019 to avoid multiple definitions
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-006: Key Derivation from Mnemonic
|
||||
*/
|
||||
|
||||
#ifndef NIP006_H
|
||||
#define NIP006_H
|
||||
|
||||
#include "nip001.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Input type detection
|
||||
typedef enum {
|
||||
NOSTR_INPUT_UNKNOWN = 0,
|
||||
NOSTR_INPUT_NSEC_HEX,
|
||||
NOSTR_INPUT_NSEC_BECH32,
|
||||
NOSTR_INPUT_MNEMONIC
|
||||
} nostr_input_type_t;
|
||||
|
||||
// Function declarations
|
||||
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);
|
||||
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
|
||||
int account, unsigned char* private_key,
|
||||
unsigned char* public_key);
|
||||
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
|
||||
unsigned char* private_key, unsigned char* public_key);
|
||||
nostr_input_type_t nostr_detect_input_type(const char* input);
|
||||
int nostr_decode_nsec(const char* input, unsigned char* private_key);
|
||||
int nostr_decode_npub(const char* input, unsigned char* public_key);
|
||||
|
||||
#endif // NIP006_H
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-011: Relay Information Document
|
||||
*/
|
||||
|
||||
#include "nip011.h"
|
||||
#include "nip005.h" // For HTTP functionality
|
||||
#include "../cjson/cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef DISABLE_NIP05 // NIP-11 uses the same HTTP infrastructure as NIP-05
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
// Maximum sizes for NIP-11 operations
|
||||
#define NIP11_MAX_URL_SIZE 512
|
||||
#define NIP11_MAX_RESPONSE_SIZE 16384
|
||||
#define NIP11_DEFAULT_TIMEOUT 10
|
||||
|
||||
// Structure for HTTP response handling (same as NIP-05)
|
||||
typedef struct {
|
||||
char* data;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} nip11_http_response_t;
|
||||
|
||||
/**
|
||||
* Callback function for curl to write HTTP response data
|
||||
*/
|
||||
static size_t nip11_write_callback(void* contents, size_t size, size_t nmemb, nip11_http_response_t* response) {
|
||||
size_t total_size = size * nmemb;
|
||||
|
||||
// Check if we need to expand the buffer
|
||||
if (response->size + total_size >= response->capacity) {
|
||||
size_t new_capacity = response->capacity * 2;
|
||||
if (new_capacity < response->size + total_size + 1) {
|
||||
new_capacity = response->size + total_size + 1;
|
||||
}
|
||||
|
||||
char* new_data = realloc(response->data, new_capacity);
|
||||
if (!new_data) {
|
||||
return 0; // Out of memory
|
||||
}
|
||||
|
||||
response->data = new_data;
|
||||
response->capacity = new_capacity;
|
||||
}
|
||||
|
||||
// Append the new data
|
||||
memcpy(response->data + response->size, contents, total_size);
|
||||
response->size += total_size;
|
||||
response->data[response->size] = '\0';
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert WebSocket URL to HTTP URL for NIP-11 document retrieval
|
||||
*/
|
||||
static char* nip11_ws_to_http_url(const char* ws_url) {
|
||||
if (!ws_url) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t url_len = strlen(ws_url);
|
||||
char* http_url = malloc(url_len + 10); // Extra space for protocol change
|
||||
if (!http_url) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert ws:// to http:// and wss:// to https://
|
||||
if (strncmp(ws_url, "ws://", 5) == 0) {
|
||||
sprintf(http_url, "http://%s", ws_url + 5);
|
||||
} else if (strncmp(ws_url, "wss://", 6) == 0) {
|
||||
sprintf(http_url, "https://%s", ws_url + 6);
|
||||
} else {
|
||||
// Assume it's already HTTP(S) or add https:// as default
|
||||
if (strncmp(ws_url, "http://", 7) == 0 || strncmp(ws_url, "https://", 8) == 0) {
|
||||
strcpy(http_url, ws_url);
|
||||
} else {
|
||||
sprintf(http_url, "https://%s", ws_url);
|
||||
}
|
||||
}
|
||||
|
||||
return http_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse supported NIPs array from JSON
|
||||
*/
|
||||
static int nip11_parse_supported_nips(cJSON* nips_array, int** nips_out, size_t* count_out) {
|
||||
if (!nips_array || !cJSON_IsArray(nips_array)) {
|
||||
*nips_out = NULL;
|
||||
*count_out = 0;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int array_size = cJSON_GetArraySize(nips_array);
|
||||
if (array_size == 0) {
|
||||
*nips_out = NULL;
|
||||
*count_out = 0;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int* nips = malloc(array_size * sizeof(int));
|
||||
if (!nips) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
int valid_count = 0;
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON* nip_item = cJSON_GetArrayItem(nips_array, i);
|
||||
if (nip_item && cJSON_IsNumber(nip_item)) {
|
||||
nips[valid_count++] = (int)cJSON_GetNumberValue(nip_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
free(nips);
|
||||
*nips_out = NULL;
|
||||
*count_out = 0;
|
||||
} else {
|
||||
*nips_out = nips;
|
||||
*count_out = valid_count;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse string array from JSON
|
||||
*/
|
||||
static int nip11_parse_string_array(cJSON* json_array, char*** strings_out, size_t* count_out) {
|
||||
if (!json_array || !cJSON_IsArray(json_array)) {
|
||||
*strings_out = NULL;
|
||||
*count_out = 0;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int array_size = cJSON_GetArraySize(json_array);
|
||||
if (array_size == 0) {
|
||||
*strings_out = NULL;
|
||||
*count_out = 0;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
char** strings = malloc(array_size * sizeof(char*));
|
||||
if (!strings) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
int valid_count = 0;
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON* string_item = cJSON_GetArrayItem(json_array, i);
|
||||
if (string_item && cJSON_IsString(string_item)) {
|
||||
const char* str_value = cJSON_GetStringValue(string_item);
|
||||
if (str_value && strlen(str_value) > 0) {
|
||||
strings[valid_count] = malloc(strlen(str_value) + 1);
|
||||
if (strings[valid_count]) {
|
||||
strcpy(strings[valid_count], str_value);
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
free(strings);
|
||||
*strings_out = NULL;
|
||||
*count_out = 0;
|
||||
} else {
|
||||
*strings_out = strings;
|
||||
*count_out = valid_count;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to safely copy JSON string value
|
||||
*/
|
||||
static char* nip11_copy_json_string(cJSON* json_item) {
|
||||
if (!json_item || !cJSON_IsString(json_item)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* str_value = cJSON_GetStringValue(json_item);
|
||||
if (!str_value) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* copy = malloc(strlen(str_value) + 1);
|
||||
if (copy) {
|
||||
strcpy(copy, str_value);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse NIP-11 relay information document from JSON
|
||||
*/
|
||||
static int nip11_parse_relay_info(const char* json_response, nostr_relay_info_t* info) {
|
||||
if (!json_response || !info) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Initialize structure
|
||||
memset(info, 0, sizeof(nostr_relay_info_t));
|
||||
|
||||
// Parse JSON
|
||||
cJSON* json = cJSON_Parse(json_response);
|
||||
if (!json) {
|
||||
return NOSTR_ERROR_NIP05_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
// Parse basic information
|
||||
info->basic.name = nip11_copy_json_string(cJSON_GetObjectItem(json, "name"));
|
||||
info->basic.description = nip11_copy_json_string(cJSON_GetObjectItem(json, "description"));
|
||||
info->basic.pubkey = nip11_copy_json_string(cJSON_GetObjectItem(json, "pubkey"));
|
||||
info->basic.contact = nip11_copy_json_string(cJSON_GetObjectItem(json, "contact"));
|
||||
info->basic.software = nip11_copy_json_string(cJSON_GetObjectItem(json, "software"));
|
||||
info->basic.version = nip11_copy_json_string(cJSON_GetObjectItem(json, "version"));
|
||||
|
||||
// Parse supported NIPs
|
||||
cJSON* supported_nips = cJSON_GetObjectItem(json, "supported_nips");
|
||||
nip11_parse_supported_nips(supported_nips, &info->basic.supported_nips, &info->basic.supported_nips_count);
|
||||
|
||||
// Parse limitations (if present)
|
||||
cJSON* limitation = cJSON_GetObjectItem(json, "limitation");
|
||||
if (limitation && cJSON_IsObject(limitation)) {
|
||||
info->has_limitations = 1;
|
||||
|
||||
cJSON* item;
|
||||
item = cJSON_GetObjectItem(limitation, "max_message_length");
|
||||
info->limitations.max_message_length = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_subscriptions");
|
||||
info->limitations.max_subscriptions = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_filters");
|
||||
info->limitations.max_filters = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_limit");
|
||||
info->limitations.max_limit = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_subid_length");
|
||||
info->limitations.max_subid_length = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "min_prefix");
|
||||
info->limitations.min_prefix = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_event_tags");
|
||||
info->limitations.max_event_tags = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "max_content_length");
|
||||
info->limitations.max_content_length = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "min_pow_difficulty");
|
||||
info->limitations.min_pow_difficulty = (item && cJSON_IsNumber(item)) ? (int)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "auth_required");
|
||||
info->limitations.auth_required = (item && cJSON_IsBool(item)) ? (cJSON_IsTrue(item) ? 1 : 0) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "payment_required");
|
||||
info->limitations.payment_required = (item && cJSON_IsBool(item)) ? (cJSON_IsTrue(item) ? 1 : 0) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "restricted_writes");
|
||||
info->limitations.restricted_writes = (item && cJSON_IsBool(item)) ? (cJSON_IsTrue(item) ? 1 : 0) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "created_at_lower_limit");
|
||||
info->limitations.created_at_lower_limit = (item && cJSON_IsNumber(item)) ? (long)cJSON_GetNumberValue(item) : -1;
|
||||
|
||||
item = cJSON_GetObjectItem(limitation, "created_at_upper_limit");
|
||||
info->limitations.created_at_upper_limit = (item && cJSON_IsNumber(item)) ? (long)cJSON_GetNumberValue(item) : -1;
|
||||
}
|
||||
|
||||
// Parse relay countries
|
||||
cJSON* relay_countries = cJSON_GetObjectItem(json, "relay_countries");
|
||||
if (relay_countries && cJSON_IsArray(relay_countries)) {
|
||||
info->has_content_limitations = 1;
|
||||
nip11_parse_string_array(relay_countries, &info->content_limitations.relay_countries,
|
||||
&info->content_limitations.relay_countries_count);
|
||||
}
|
||||
|
||||
// Parse community preferences
|
||||
cJSON* language_tags = cJSON_GetObjectItem(json, "language_tags");
|
||||
cJSON* tags = cJSON_GetObjectItem(json, "tags");
|
||||
cJSON* posting_policy = cJSON_GetObjectItem(json, "posting_policy");
|
||||
|
||||
if ((language_tags && cJSON_IsArray(language_tags)) ||
|
||||
(tags && cJSON_IsArray(tags)) ||
|
||||
(posting_policy && cJSON_IsString(posting_policy))) {
|
||||
info->has_community_preferences = 1;
|
||||
|
||||
if (language_tags) {
|
||||
nip11_parse_string_array(language_tags, &info->community_preferences.language_tags,
|
||||
&info->community_preferences.language_tags_count);
|
||||
}
|
||||
|
||||
if (tags) {
|
||||
nip11_parse_string_array(tags, &info->community_preferences.tags,
|
||||
&info->community_preferences.tags_count);
|
||||
}
|
||||
|
||||
if (posting_policy) {
|
||||
info->community_preferences.posting_policy = nip11_copy_json_string(posting_policy);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse icon
|
||||
cJSON* icon = cJSON_GetObjectItem(json, "icon");
|
||||
if (icon && cJSON_IsString(icon)) {
|
||||
info->has_icon = 1;
|
||||
info->icon.icon = nip11_copy_json_string(icon);
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch relay information document from a relay URL
|
||||
*/
|
||||
int nostr_nip11_fetch_relay_info(const char* relay_url, nostr_relay_info_t** info_out, int timeout_seconds) {
|
||||
if (!relay_url || !info_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Convert WebSocket URL to HTTP URL
|
||||
char* http_url = nip11_ws_to_http_url(relay_url);
|
||||
if (!http_url) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
// Allocate info structure
|
||||
nostr_relay_info_t* info = malloc(sizeof(nostr_relay_info_t));
|
||||
if (!info) {
|
||||
free(http_url);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
// Make HTTP request with NIP-11 required header
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
free(http_url);
|
||||
free(info);
|
||||
return NOSTR_ERROR_NETWORK_FAILED;
|
||||
}
|
||||
|
||||
// Use the HTTP response structure
|
||||
nip11_http_response_t response = {0};
|
||||
response.capacity = 1024;
|
||||
response.data = malloc(response.capacity);
|
||||
if (!response.data) {
|
||||
curl_easy_cleanup(curl);
|
||||
free(http_url);
|
||||
free(info);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
response.data[0] = '\0';
|
||||
|
||||
// Set up headers for NIP-11
|
||||
struct curl_slist* headers = NULL;
|
||||
headers = curl_slist_append(headers, "Accept: application/nostr+json");
|
||||
|
||||
// Set curl options - use proper function pointer cast
|
||||
curl_easy_setopt(curl, CURLOPT_URL, http_url);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)nip11_write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)(timeout_seconds > 0 ? timeout_seconds : NIP11_DEFAULT_TIMEOUT));
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // NIP-11 allows redirects
|
||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "nostr-core/1.0");
|
||||
|
||||
// Perform the request
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
long response_code = 0;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
free(http_url);
|
||||
|
||||
if (res != CURLE_OK || response_code != 200) {
|
||||
free(response.data);
|
||||
free(info);
|
||||
return NOSTR_ERROR_NIP05_HTTP_FAILED;
|
||||
}
|
||||
|
||||
// Parse the relay information
|
||||
int parse_result = nip11_parse_relay_info(response.data, info);
|
||||
free(response.data);
|
||||
|
||||
if (parse_result != NOSTR_SUCCESS) {
|
||||
nostr_nip11_relay_info_free(info);
|
||||
return parse_result;
|
||||
}
|
||||
|
||||
*info_out = info;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free relay information structure
|
||||
*/
|
||||
void nostr_nip11_relay_info_free(nostr_relay_info_t* info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Free basic info strings
|
||||
free(info->basic.name);
|
||||
free(info->basic.description);
|
||||
free(info->basic.pubkey);
|
||||
free(info->basic.contact);
|
||||
free(info->basic.software);
|
||||
free(info->basic.version);
|
||||
free(info->basic.supported_nips);
|
||||
|
||||
// Free content limitations
|
||||
if (info->has_content_limitations) {
|
||||
for (size_t i = 0; i < info->content_limitations.relay_countries_count; i++) {
|
||||
free(info->content_limitations.relay_countries[i]);
|
||||
}
|
||||
free(info->content_limitations.relay_countries);
|
||||
}
|
||||
|
||||
// Free community preferences
|
||||
if (info->has_community_preferences) {
|
||||
for (size_t i = 0; i < info->community_preferences.language_tags_count; i++) {
|
||||
free(info->community_preferences.language_tags[i]);
|
||||
}
|
||||
free(info->community_preferences.language_tags);
|
||||
|
||||
for (size_t i = 0; i < info->community_preferences.tags_count; i++) {
|
||||
free(info->community_preferences.tags[i]);
|
||||
}
|
||||
free(info->community_preferences.tags);
|
||||
|
||||
free(info->community_preferences.posting_policy);
|
||||
}
|
||||
|
||||
// Free icon
|
||||
if (info->has_icon) {
|
||||
free(info->icon.icon);
|
||||
}
|
||||
|
||||
free(info);
|
||||
}
|
||||
|
||||
#else // DISABLE_NIP05
|
||||
|
||||
/**
|
||||
* Stub implementations when NIP-05 is disabled at compile time
|
||||
*/
|
||||
int nostr_nip11_fetch_relay_info(const char* relay_url, nostr_relay_info_t** info_out, int timeout_seconds) {
|
||||
(void)relay_url;
|
||||
(void)info_out;
|
||||
(void)timeout_seconds;
|
||||
return NOSTR_ERROR_NETWORK_FAILED; // NIP-11 disabled at compile time
|
||||
}
|
||||
|
||||
void nostr_nip11_relay_info_free(nostr_relay_info_t* info) {
|
||||
(void)info;
|
||||
// No-op when disabled
|
||||
}
|
||||
|
||||
#endif // DISABLE_NIP05
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-011: Relay Information Document
|
||||
*/
|
||||
|
||||
#ifndef NIP011_H
|
||||
#define NIP011_H
|
||||
|
||||
#include "nip001.h"
|
||||
#include <stddef.h>
|
||||
|
||||
// NIP-11 Relay Information structures
|
||||
|
||||
// Basic relay information
|
||||
typedef struct {
|
||||
char* name;
|
||||
char* description;
|
||||
char* pubkey;
|
||||
char* contact;
|
||||
char* software;
|
||||
char* version;
|
||||
int* supported_nips;
|
||||
size_t supported_nips_count;
|
||||
} nostr_relay_basic_info_t;
|
||||
|
||||
// Relay limitations
|
||||
typedef struct {
|
||||
int max_message_length;
|
||||
int max_subscriptions;
|
||||
int max_filters;
|
||||
int max_limit;
|
||||
int max_subid_length;
|
||||
int min_prefix;
|
||||
int max_event_tags;
|
||||
int max_content_length;
|
||||
int min_pow_difficulty;
|
||||
int auth_required; // -1 = not specified, 0 = false, 1 = true
|
||||
int payment_required; // -1 = not specified, 0 = false, 1 = true
|
||||
int restricted_writes; // -1 = not specified, 0 = false, 1 = true
|
||||
long created_at_lower_limit;
|
||||
long created_at_upper_limit;
|
||||
} nostr_relay_limitations_t;
|
||||
|
||||
// Content limitations
|
||||
typedef struct {
|
||||
char** relay_countries;
|
||||
size_t relay_countries_count;
|
||||
} nostr_relay_content_limitations_t;
|
||||
|
||||
// Community preferences
|
||||
typedef struct {
|
||||
char** language_tags;
|
||||
size_t language_tags_count;
|
||||
char** tags;
|
||||
size_t tags_count;
|
||||
char* posting_policy;
|
||||
} nostr_relay_community_preferences_t;
|
||||
|
||||
// Icon information
|
||||
typedef struct {
|
||||
char* icon;
|
||||
} nostr_relay_icon_t;
|
||||
|
||||
// Complete relay information structure
|
||||
typedef struct {
|
||||
nostr_relay_basic_info_t basic;
|
||||
|
||||
int has_limitations;
|
||||
nostr_relay_limitations_t limitations;
|
||||
|
||||
int has_content_limitations;
|
||||
nostr_relay_content_limitations_t content_limitations;
|
||||
|
||||
int has_community_preferences;
|
||||
nostr_relay_community_preferences_t community_preferences;
|
||||
|
||||
int has_icon;
|
||||
nostr_relay_icon_t icon;
|
||||
} nostr_relay_info_t;
|
||||
|
||||
// Function declarations
|
||||
int nostr_nip11_fetch_relay_info(const char* relay_url, nostr_relay_info_t** info_out, int timeout_seconds);
|
||||
void nostr_nip11_relay_info_free(nostr_relay_info_t* info);
|
||||
|
||||
#endif // NIP011_H
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-013: Proof of Work
|
||||
*/
|
||||
|
||||
#include "nip013.h"
|
||||
#include "nip001.h"
|
||||
#include "utils.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Count leading zero bits in a hash (NIP-13 reference implementation)
|
||||
*/
|
||||
static int zero_bits(unsigned char b) {
|
||||
int n = 0;
|
||||
|
||||
if (b == 0)
|
||||
return 8;
|
||||
|
||||
while (b >>= 1)
|
||||
n++;
|
||||
|
||||
return 7-n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the number of leading zero bits in a hash (NIP-13 reference implementation)
|
||||
*/
|
||||
static int count_leading_zero_bits(unsigned char *hash) {
|
||||
int bits, total, i;
|
||||
for (i = 0, total = 0; i < 32; i++) {
|
||||
bits = zero_bits(hash[i]);
|
||||
total += bits;
|
||||
if (bits != 8)
|
||||
break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update nonce tag with target difficulty
|
||||
*/
|
||||
static int update_nonce_tag_with_difficulty(cJSON* tags, uint64_t nonce, int target_difficulty) {
|
||||
if (!tags) return -1;
|
||||
|
||||
// Look for existing nonce tag and remove it
|
||||
cJSON* tag = NULL;
|
||||
int index = 0;
|
||||
cJSON_ArrayForEach(tag, tags) {
|
||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
||||
cJSON* tag_type = cJSON_GetArrayItem(tag, 0);
|
||||
if (tag_type && cJSON_IsString(tag_type) &&
|
||||
strcmp(cJSON_GetStringValue(tag_type), "nonce") == 0) {
|
||||
// Remove existing nonce tag
|
||||
cJSON_DetachItemFromArray(tags, index);
|
||||
cJSON_Delete(tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// Add new nonce tag with format: ["nonce", "<nonce>", "<target_difficulty>"]
|
||||
cJSON* nonce_tag = cJSON_CreateArray();
|
||||
if (!nonce_tag) return -1;
|
||||
|
||||
char nonce_str[32];
|
||||
char difficulty_str[16];
|
||||
snprintf(nonce_str, sizeof(nonce_str), "%llu", (unsigned long long)nonce);
|
||||
snprintf(difficulty_str, sizeof(difficulty_str), "%d", target_difficulty);
|
||||
|
||||
cJSON_AddItemToArray(nonce_tag, cJSON_CreateString("nonce"));
|
||||
cJSON_AddItemToArray(nonce_tag, cJSON_CreateString(nonce_str));
|
||||
cJSON_AddItemToArray(nonce_tag, cJSON_CreateString(difficulty_str));
|
||||
|
||||
cJSON_AddItemToArray(tags, nonce_tag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to replace event content with successful PoW result
|
||||
*/
|
||||
static void replace_event_content(cJSON* target_event, cJSON* source_event) {
|
||||
// Remove old fields
|
||||
cJSON_DeleteItemFromObject(target_event, "id");
|
||||
cJSON_DeleteItemFromObject(target_event, "sig");
|
||||
cJSON_DeleteItemFromObject(target_event, "tags");
|
||||
cJSON_DeleteItemFromObject(target_event, "created_at");
|
||||
|
||||
// Copy new fields from successful event
|
||||
cJSON* id = cJSON_GetObjectItem(source_event, "id");
|
||||
cJSON* sig = cJSON_GetObjectItem(source_event, "sig");
|
||||
cJSON* tags = cJSON_GetObjectItem(source_event, "tags");
|
||||
cJSON* created_at = cJSON_GetObjectItem(source_event, "created_at");
|
||||
|
||||
if (id) cJSON_AddItemToObject(target_event, "id", cJSON_Duplicate(id, 1));
|
||||
if (sig) cJSON_AddItemToObject(target_event, "sig", cJSON_Duplicate(sig, 1));
|
||||
if (tags) cJSON_AddItemToObject(target_event, "tags", cJSON_Duplicate(tags, 1));
|
||||
if (created_at) cJSON_AddItemToObject(target_event, "created_at", cJSON_Duplicate(created_at, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add NIP-13 Proof of Work to an event
|
||||
*
|
||||
* @param event The event to add proof of work to
|
||||
* @param private_key The private key for re-signing the event
|
||||
* @param target_difficulty Target number of leading zero bits (default: 4 if 0)
|
||||
* @param max_attempts Maximum number of mining attempts (default: 10,000,000 if <= 0)
|
||||
* @param progress_report_interval How often to call progress callback (default: 10,000 if <= 0)
|
||||
* @param timestamp_update_interval How often to update timestamp (default: 10,000 if <= 0)
|
||||
* @param progress_callback Optional callback for mining progress
|
||||
* @param user_data User data for progress callback
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_add_proof_of_work(cJSON* event, const unsigned char* private_key,
|
||||
int target_difficulty, int max_attempts,
|
||||
int progress_report_interval, int timestamp_update_interval,
|
||||
void (*progress_callback)(int current_difficulty, uint64_t nonce, void* user_data),
|
||||
void* user_data) {
|
||||
if (!event || !private_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// Set default difficulty if not specified (but allow 0 to disable PoW)
|
||||
if (target_difficulty < 0) {
|
||||
target_difficulty = 4;
|
||||
}
|
||||
|
||||
// If target_difficulty is 0, skip proof of work entirely
|
||||
if (target_difficulty == 0) {
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
// Set default values for parameters
|
||||
if (max_attempts <= 0) {
|
||||
max_attempts = 10000000; // 10 million default
|
||||
}
|
||||
if (progress_report_interval <= 0) {
|
||||
progress_report_interval = 10000; // Every 10,000 attempts
|
||||
}
|
||||
if (timestamp_update_interval <= 0) {
|
||||
timestamp_update_interval = 10000; // Every 10,000 attempts
|
||||
}
|
||||
|
||||
// Extract event data for reconstruction
|
||||
cJSON* kind_item = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* content_item = cJSON_GetObjectItem(event, "content");
|
||||
cJSON* created_at_item = cJSON_GetObjectItem(event, "created_at");
|
||||
cJSON* tags_item = cJSON_GetObjectItem(event, "tags");
|
||||
|
||||
if (!kind_item || !content_item || !created_at_item || !tags_item) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
int kind = (int)cJSON_GetNumberValue(kind_item);
|
||||
const char* content = cJSON_GetStringValue(content_item);
|
||||
time_t original_timestamp = (time_t)cJSON_GetNumberValue(created_at_item);
|
||||
|
||||
uint64_t nonce = 0;
|
||||
int attempts = 0;
|
||||
time_t current_timestamp = original_timestamp;
|
||||
|
||||
// PoW difficulty tracking variables
|
||||
int best_difficulty_this_round = 0;
|
||||
int best_difficulty_overall = 0;
|
||||
|
||||
// Mining loop
|
||||
while (attempts < max_attempts) {
|
||||
// Update timestamp based on timestamp_update_interval
|
||||
if (attempts % timestamp_update_interval == 0) {
|
||||
current_timestamp = time(NULL);
|
||||
#ifdef ENABLE_DEBUG_LOGGING
|
||||
FILE* f = fopen("debug.log", "a");
|
||||
if (f) {
|
||||
fprintf(f, "PoW mining: %d attempts, best this round: %d, overall best: %d, goal: %d\n",
|
||||
attempts, best_difficulty_this_round, best_difficulty_overall, target_difficulty);
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
// Reset best difficulty for the new round
|
||||
best_difficulty_this_round = 0;
|
||||
}
|
||||
|
||||
// Call progress callback at specified intervals
|
||||
if (progress_callback && (attempts % progress_report_interval == 0)) {
|
||||
progress_callback(best_difficulty_overall, nonce, user_data);
|
||||
}
|
||||
|
||||
// Create working copy of tags and add nonce
|
||||
cJSON* working_tags = cJSON_Duplicate(tags_item, 1);
|
||||
if (!working_tags) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
if (update_nonce_tag_with_difficulty(working_tags, nonce, target_difficulty) != 0) {
|
||||
cJSON_Delete(working_tags);
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Create and sign new event with current nonce
|
||||
cJSON* test_event = nostr_create_and_sign_event(kind, content, working_tags,
|
||||
private_key, current_timestamp);
|
||||
cJSON_Delete(working_tags);
|
||||
|
||||
if (!test_event) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Check PoW difficulty
|
||||
cJSON* id_item = cJSON_GetObjectItem(test_event, "id");
|
||||
if (!id_item || !cJSON_IsString(id_item)) {
|
||||
cJSON_Delete(test_event);
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
const char* event_id = cJSON_GetStringValue(id_item);
|
||||
unsigned char hash[32];
|
||||
if (nostr_hex_to_bytes(event_id, hash, 32) != NOSTR_SUCCESS) {
|
||||
cJSON_Delete(test_event);
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
// Count leading zero bits using NIP-13 method
|
||||
int current_difficulty = count_leading_zero_bits(hash);
|
||||
|
||||
// Update difficulty tracking
|
||||
if (current_difficulty > best_difficulty_this_round) {
|
||||
best_difficulty_this_round = current_difficulty;
|
||||
}
|
||||
if (current_difficulty > best_difficulty_overall) {
|
||||
best_difficulty_overall = current_difficulty;
|
||||
}
|
||||
|
||||
// Check if we've reached the target
|
||||
if (current_difficulty >= target_difficulty) {
|
||||
#ifdef ENABLE_DEBUG_LOGGING
|
||||
FILE* f = fopen("debug.log", "a");
|
||||
if (f) {
|
||||
fprintf(f, "PoW SUCCESS: Found difficulty %d (target %d) at nonce %llu after %d attempts\n",
|
||||
current_difficulty, target_difficulty, (unsigned long long)nonce, attempts + 1);
|
||||
|
||||
// Print the final event JSON
|
||||
char* event_json = cJSON_Print(test_event);
|
||||
if (event_json) {
|
||||
fprintf(f, "Final event: %s\n", event_json);
|
||||
free(event_json);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy successful result back to input event
|
||||
replace_event_content(event, test_event);
|
||||
cJSON_Delete(test_event);
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
cJSON_Delete(test_event);
|
||||
nonce++;
|
||||
attempts++;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUG_LOGGING
|
||||
// Debug logging - failure
|
||||
FILE* f = fopen("debug.log", "a");
|
||||
if (f) {
|
||||
fprintf(f, "PoW FAILED: Mining failed after %d attempts\n", max_attempts);
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we reach here, we've exceeded max attempts
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-013: Proof of Work
|
||||
*/
|
||||
|
||||
#ifndef NIP013_H
|
||||
#define NIP013_H
|
||||
|
||||
#include "nip001.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Function declarations
|
||||
int nostr_add_proof_of_work(cJSON* event, const unsigned char* private_key,
|
||||
int target_difficulty, int max_attempts,
|
||||
int progress_report_interval, int timestamp_update_interval,
|
||||
void (*progress_callback)(int current_difficulty, uint64_t nonce, void* user_data),
|
||||
void* user_data);
|
||||
|
||||
#endif // NIP013_H
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-019: Bech32-encoded Entities
|
||||
*/
|
||||
|
||||
#include "nip019.h"
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define BECH32_CONST 1
|
||||
|
||||
static const char bech32_charset[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
static const int8_t bech32_charset_rev[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
static uint32_t bech32_polymod_step(uint32_t pre) {
|
||||
uint8_t b = pre >> 25;
|
||||
return ((pre & 0x1FFFFFF) << 5) ^
|
||||
(-((b >> 0) & 1) & 0x3b6a57b2UL) ^
|
||||
(-((b >> 1) & 1) & 0x26508e6dUL) ^
|
||||
(-((b >> 2) & 1) & 0x1ea119faUL) ^
|
||||
(-((b >> 3) & 1) & 0x3d4233ddUL) ^
|
||||
(-((b >> 4) & 1) & 0x2a1462b3UL);
|
||||
}
|
||||
|
||||
static int convert_bits(uint8_t *out, size_t *outlen, int outbits, const uint8_t *in, size_t inlen, int inbits, int pad) {
|
||||
uint32_t val = 0;
|
||||
int bits = 0;
|
||||
uint32_t maxv = (((uint32_t)1) << outbits) - 1;
|
||||
*outlen = 0;
|
||||
while (inlen--) {
|
||||
val = (val << inbits) | *(in++);
|
||||
bits += inbits;
|
||||
while (bits >= outbits) {
|
||||
bits -= outbits;
|
||||
out[(*outlen)++] = (val >> bits) & maxv;
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits) {
|
||||
out[(*outlen)++] = (val << (outbits - bits)) & maxv;
|
||||
}
|
||||
} else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len) {
|
||||
uint32_t chk = 1;
|
||||
size_t i, hrp_len = strlen(hrp);
|
||||
|
||||
for (i = 0; i < hrp_len; ++i) {
|
||||
int ch = hrp[i];
|
||||
if (ch < 33 || ch > 126) return 0;
|
||||
if (ch >= 'A' && ch <= 'Z') return 0;
|
||||
chk = bech32_polymod_step(chk) ^ (ch >> 5);
|
||||
}
|
||||
|
||||
chk = bech32_polymod_step(chk);
|
||||
for (i = 0; i < hrp_len; ++i) {
|
||||
chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
|
||||
*(output++) = hrp[i];
|
||||
}
|
||||
|
||||
*(output++) = '1';
|
||||
for (i = 0; i < data_len; ++i) {
|
||||
if (*data >> 5) return 0;
|
||||
chk = bech32_polymod_step(chk) ^ (*data);
|
||||
*(output++) = bech32_charset[*(data++)];
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; ++i) {
|
||||
chk = bech32_polymod_step(chk);
|
||||
}
|
||||
|
||||
chk ^= BECH32_CONST;
|
||||
for (i = 0; i < 6; ++i) {
|
||||
*(output++) = bech32_charset[(chk >> ((5 - i) * 5)) & 0x1f];
|
||||
}
|
||||
|
||||
*output = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bech32_decode(const char* input, const char* hrp, unsigned char* data, size_t* data_len) {
|
||||
if (!input || !hrp || !data || !data_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t input_len = strlen(input);
|
||||
size_t hrp_len = strlen(hrp);
|
||||
|
||||
if (input_len < hrp_len + 7) return 0;
|
||||
if (strncmp(input, hrp, hrp_len) != 0) return 0;
|
||||
if (input[hrp_len] != '1') return 0;
|
||||
|
||||
const char* data_part = input + hrp_len + 1;
|
||||
size_t data_part_len = input_len - hrp_len - 1;
|
||||
|
||||
uint8_t values[256];
|
||||
for (size_t i = 0; i < data_part_len; i++) {
|
||||
unsigned char c = (unsigned char)data_part[i];
|
||||
if (c >= 128) return 0;
|
||||
int8_t val = bech32_charset_rev[c];
|
||||
if (val == -1) return 0;
|
||||
values[i] = (uint8_t)val;
|
||||
}
|
||||
|
||||
if (data_part_len < 6) return 0;
|
||||
|
||||
uint32_t chk = 1;
|
||||
for (size_t i = 0; i < hrp_len; i++) {
|
||||
chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5);
|
||||
}
|
||||
chk = bech32_polymod_step(chk);
|
||||
for (size_t i = 0; i < hrp_len; i++) {
|
||||
chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
|
||||
}
|
||||
for (size_t i = 0; i < data_part_len; i++) {
|
||||
chk = bech32_polymod_step(chk) ^ values[i];
|
||||
}
|
||||
|
||||
if (chk != BECH32_CONST) return 0;
|
||||
|
||||
size_t payload_len = data_part_len - 6;
|
||||
size_t decoded_len;
|
||||
if (!convert_bits(data, &decoded_len, 8, values, payload_len, 5, 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*data_len = decoded_len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output) {
|
||||
if (!key || !hrp || !output) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
uint8_t conv[64];
|
||||
size_t conv_len;
|
||||
|
||||
if (!convert_bits(conv, &conv_len, 5, key, 32, 8, 1)) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
if (!bech32_encode(output, hrp, conv, conv_len)) {
|
||||
return NOSTR_ERROR_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
nostr_input_type_t nostr_detect_input_type(const char* input) {
|
||||
if (!input || strlen(input) == 0) {
|
||||
return NOSTR_INPUT_UNKNOWN;
|
||||
}
|
||||
|
||||
size_t len = strlen(input);
|
||||
|
||||
// Check for bech32 nsec
|
||||
if (len > 5 && strncmp(input, "nsec1", 5) == 0) {
|
||||
return NOSTR_INPUT_NSEC_BECH32;
|
||||
}
|
||||
|
||||
// Check for hex nsec (64 characters, all hex)
|
||||
if (len == 64) {
|
||||
int is_hex = 1;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!isxdigit(input[i])) {
|
||||
is_hex = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_hex) {
|
||||
return NOSTR_INPUT_NSEC_HEX;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for mnemonic (space-separated words)
|
||||
int word_count = 0;
|
||||
char temp[1024];
|
||||
strncpy(temp, input, sizeof(temp) - 1);
|
||||
temp[sizeof(temp) - 1] = '\0';
|
||||
|
||||
char* token = strtok(temp, " ");
|
||||
while (token != NULL) {
|
||||
word_count++;
|
||||
token = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
// BIP39 mnemonics are typically 12, 18, or 24 words
|
||||
if (word_count >= 12 && word_count <= 24) {
|
||||
return NOSTR_INPUT_MNEMONIC;
|
||||
}
|
||||
|
||||
return NOSTR_INPUT_UNKNOWN;
|
||||
}
|
||||
|
||||
int nostr_decode_nsec(const char* input, unsigned char* private_key) {
|
||||
if (!input || !private_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
nostr_input_type_t type = nostr_detect_input_type(input);
|
||||
|
||||
if (type == NOSTR_INPUT_NSEC_HEX) {
|
||||
if (nostr_hex_to_bytes(input, private_key, 32) != NOSTR_SUCCESS) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
} else if (type == NOSTR_INPUT_NSEC_BECH32) {
|
||||
size_t decoded_len;
|
||||
if (!bech32_decode(input, "nsec", private_key, &decoded_len)) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
if (decoded_len != 32) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
} else {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
// TODO: Add private key validation if crypto functions are available
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int nostr_decode_npub(const char* input, unsigned char* public_key) {
|
||||
if (!input || !public_key) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
nostr_input_type_t type = nostr_detect_input_type(input);
|
||||
|
||||
if (type == NOSTR_INPUT_NSEC_HEX) { // Actually public key hex
|
||||
if (nostr_hex_to_bytes(input, public_key, 32) != NOSTR_SUCCESS) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
} else if (strncmp(input, "npub1", 4) == 0) { // Bech32 npub
|
||||
size_t decoded_len;
|
||||
if (!bech32_decode(input, "npub", public_key, &decoded_len)) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
if (decoded_len != 32) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
} else {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* NOSTR Core Library - NIP-019: Bech32-encoded Entities
|
||||
*/
|
||||
|
||||
#ifndef NIP019_H
|
||||
#define NIP019_H
|
||||
|
||||
#include "nip001.h"
|
||||
#include "nip006.h" // For nostr_input_type_t enum
|
||||
|
||||
// Function declarations
|
||||
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
|
||||
nostr_input_type_t nostr_detect_input_type(const char* input);
|
||||
int nostr_decode_nsec(const char* input, unsigned char* private_key);
|
||||
int nostr_decode_npub(const char* input, unsigned char* public_key);
|
||||
|
||||
#endif // NIP019_H
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* NOSTR Core - Common Definitions
|
||||
* Shared error constants and basic types for the modular NOSTR library
|
||||
*/
|
||||
|
||||
#ifndef NOSTR_COMMON_H
|
||||
#define NOSTR_COMMON_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Return codes
|
||||
#define NOSTR_SUCCESS 0
|
||||
#define NOSTR_ERROR_INVALID_INPUT -1
|
||||
#define NOSTR_ERROR_CRYPTO_FAILED -2
|
||||
#define NOSTR_ERROR_MEMORY_FAILED -3
|
||||
#define NOSTR_ERROR_IO_FAILED -4
|
||||
#define NOSTR_ERROR_NETWORK_FAILED -5
|
||||
#define NOSTR_ERROR_NIP04_INVALID_FORMAT -10
|
||||
#define NOSTR_ERROR_NIP04_DECRYPT_FAILED -11
|
||||
#define NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL -12
|
||||
#define NOSTR_ERROR_NIP44_INVALID_FORMAT -13
|
||||
#define NOSTR_ERROR_NIP44_DECRYPT_FAILED -14
|
||||
#define NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL -15
|
||||
#define NOSTR_ERROR_NIP05_INVALID_IDENTIFIER -16
|
||||
#define NOSTR_ERROR_NIP05_HTTP_FAILED -17
|
||||
#define NOSTR_ERROR_NIP05_JSON_PARSE_FAILED -18
|
||||
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -19
|
||||
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -20
|
||||
|
||||
// Constants
|
||||
#define NOSTR_PRIVATE_KEY_SIZE 32
|
||||
#define NOSTR_PUBLIC_KEY_SIZE 32
|
||||
#define NOSTR_HEX_KEY_SIZE 65 // 64 + null terminator
|
||||
#define NOSTR_BECH32_KEY_SIZE 100
|
||||
#define NOSTR_MAX_CONTENT_SIZE 2048
|
||||
#define NOSTR_MAX_URL_SIZE 256
|
||||
|
||||
// NIP-04 Constants
|
||||
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||
|
||||
// NIP-44 Constants
|
||||
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65536 // 64KB max plaintext (matches crypto header)
|
||||
|
||||
#endif // NOSTR_COMMON_H
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* NOSTR Core Library - Utilities
|
||||
*
|
||||
* General utility functions used across multiple NIPs
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Convert bytes to hexadecimal string
|
||||
*/
|
||||
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
sprintf(hex + i * 2, "%02x", bytes[i]);
|
||||
}
|
||||
hex[len * 2] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hexadecimal string to bytes
|
||||
*/
|
||||
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
|
||||
if (strlen(hex) != len * 2) {
|
||||
return -1; // NOSTR_ERROR_INVALID_INPUT
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (sscanf(hex + i * 2, "%02hhx", &bytes[i]) != 1) {
|
||||
return -1; // NOSTR_ERROR_INVALID_INPUT
|
||||
}
|
||||
}
|
||||
return 0; // NOSTR_SUCCESS
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* NOSTR Core Library - Utilities
|
||||
*
|
||||
* General utility functions used across multiple NIPs
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Error codes (imported from nip001.h)
|
||||
#define NOSTR_SUCCESS 0
|
||||
#define NOSTR_ERROR_INVALID_INPUT -1
|
||||
|
||||
// Utility function declarations
|
||||
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
|
||||
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
|
||||
|
||||
#endif // UTILS_H
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[10:59:17.573] SEND nostr.mom:443: ["REQ", "sync_0_1755183556", {
|
||||
"kinds": [1],
|
||||
"limit": 1
|
||||
}]
|
||||
[10:59:17.726] RECV nostr.mom:443: ["EVENT","sync_0_1755183556",{"content":"#kinostr #odysee #onepunchman\n\nhttps://odysee.com/@AllOverTheFilms:6/One-Punch-Man-(Season-1)---Episode-02--English-Sub-:f\n\n https://blossom.primal.net/77c18e2d7c0da3169baa9bf9161462e12f6f1e569a0863341df33c55ca41f425.jpg \n\nnostr:nprofile1qy88wumn8ghj7mn0wvhxcmmv9uq32amnwvaz7tmjv4kxz7fwv3sk6atn9e5k7tcqypwdt7q993nerey8nu8ymwgngewhz82ltlsvp2ueqjwxqex95w26yja9ph4 ","created_at":1755183592,"id":"e728318c90e8afd0b8769188260a3960ed9d6425d35f0768bd6e60dfcf21f626","kind":1,"pubkey":"362ebffa895acb0aa4ec2f11959b1c233aec2275f61b3beee19b1b6e492e2719","sig":"930d967dcb413eb02d6326778cdcc291cda10e376e0916d28d422759aa62288dbadc1722f4eccde54be87aa203d70f3ac733cef794b952f1965f43dbacedd1da","tags":[["t","kinostr"],["t","odysee"],["t","onepunchman"],["p","5cd5f8052c6791e4879f0e4db913465d711d5f5fe0c0ab99049c6064c5a395a2","wss://nos.lol/","mention"]]}]
|
||||
[10:59:17.726] SEND nostr.mom:443: ["CLOSE", "sync_0_1755183556"]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[12:00:03.609] SEND nostr.mom:443: ["REQ", "sync_0_1755187202", {
|
||||
"kinds": [1],
|
||||
"limit": 1
|
||||
}]
|
||||
[12:00:03.762] RECV nostr.mom:443: ["EVENT","sync_0_1755187202",{"content":"Hi.\n\nFor some job ads we get help from third party services during the first screening process, as it often happens that we receive thousands of applications and it becomes complex to handle them by hand one by one.\n\nIf you think there was an error, please email jobs@relai.app","created_at":1755187188,"id":"d0a6c2d9d20c97b12375d55370bbd778d064decf5c60e2815d7b3fc1683b4b27","kind":1,"pubkey":"80043aa9b23b0d1511b49618cfc48be1ab4b7df95a13a2289b9336df7c1be3b6","sig":"2917afcea4497dc14bb31a8966ee6e1a2f04f5e242c439584f172e6a3b6067412384ea5dc66c1e366100333ca20cc0b59e3919de392a4da4ae62c255711edd00","tags":[["e","a46f5b47ba44568554ef946df7b84b54156e97e9fb37f44c5575df73a7750fc7","","root"],["p","cf7ad05f8e99de8eadbbfbd5ca1c0f9b75499bce07074966b277688ca5e1d726"]]}]
|
||||
[12:00:03.762] SEND nostr.mom:443: ["CLOSE", "sync_0_1755187202"]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[12:45:51.209] SEND nostr.mom:443: ["REQ", "sync_0_1755189950", {
|
||||
"kinds": [1],
|
||||
"limit": 1
|
||||
}]
|
||||
[12:45:51.362] RECV nostr.mom:443: ["EVENT","sync_0_1755189950",{"content":"It is getting pretty bad outside. Smells like a campfire and ash is falling. It is supposed to rain today... https://image.nostr.build/b9a5f0e4c0f1b53e04dbdc3b13e687beb8ee5f8d003734cbe963c87e8fcfe3f0.jpg","created_at":1755189940,"id":"3f13cc0b3b267a8af31250b2fafad480293baaad84ff12d3ce7242bdd355be6d","kind":1,"pubkey":"101a112c8adc2e69e0003114ff1c1d36b7fcde06d84d47968e599d558721b0df","sig":"0733af7fc26271f8bfa2c4f2e54eafe6984a73f3619a8941acd57b31a9ad4805c20c44e361b7a745f52e2859675323962ddd2c9fe0e4a73faa498d36958e395d","tags":[["r","https://image.nostr.build/b9a5f0e4c0f1b53e04dbdc3b13e687beb8ee5f8d003734cbe963c87e8fcfe3f0.jpg"],["imeta","url https://image.nostr.build/b9a5f0e4c0f1b53e04dbdc3b13e687beb8ee5f8d003734cbe963c87e8fcfe3f0.jpg","x f32764ccb275b93b897b01b87000a3ac1811c0fee3d9cc1046c2254fc8f9dac2","size 369085","m image/jpeg","dim 1512x2016","blurhash _lHBrP-;Rjt7a}WBj[_4%MRjt7j[WBj[^+t7Rjj[j[azoLR,WBWBWCj[ofofRjRjWBayayj[j[RjWBs:ofWBfPj[bHofofofWBfQfQt6j[j[ofayazayWVayayfQj[j[ay","ox f32764ccb275b93b897b01b87000a3ac1811c0fee3d9cc1046c2254fc8f9dac2","alt "]]}]
|
||||
[12:45:51.362] SEND nostr.mom:443: ["CLOSE", "sync_0_1755189950"]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[19:11:31.192] SEND nostr.mom:443: ["REQ", "sync_0_1755213090", {
|
||||
"kinds": [1],
|
||||
"limit": 1
|
||||
}]
|
||||
[19:11:31.346] RECV nostr.mom:443: ["EVENT","sync_0_1755213090",{"content":"When you finally find a way to protect your money from inflation everything changes.\n\nYou don't have to constantly be looking to take risks to invest your money so that it doesn't lose value anymore because now it will keep its value.\n\nBitcoin is the last frontier of money, because its value will increase faster than any other money or investment you can make because it is global and extremely rare.\n\nBitcoin is not only money that protects you, but when you have bitcoin it changes your mindset from a poverty mindset to a wealth mindset.\n\nWhen you have bitcoin you start to think differently because it gives you your time back, every time its value goes up, the more time you have to plan your long-term life.","created_at":1755213082,"id":"b70ef3a737a4cc4b909d24018e01b3264419d8b62f189a8c36b2954134b52839","kind":1,"pubkey":"ad558fb5cbf53d44f00b1f73ebb976ce7e4cb425b6b3739a4096bca3c3f355a8","sig":"91271f6e45023b0d16464e8898b16c42f2ae605a67766d522a0375ee303c4e3229fa43154b7e164fdab74e387ccd9f5060aff9d1e21a80eb8fab72154c8b11b1","tags":[]}]
|
||||
[19:11:31.346] SEND nostr.mom:443: ["CLOSE", "sync_0_1755213090"]
|
||||
|
||||
=== NOSTR WebSocket Debug Log Started ===
|
||||
[19:15:31.278] SEND nostr.mom:443: ["REQ", "sync_0_1755213330", {
|
||||
"kinds": [1],
|
||||
"limit": 1
|
||||
}]
|
||||
[19:15:31.497] RECV nostr.mom:443: ["EVENT","sync_0_1755213330",{"content":"Quando eu pego um nota de dinheiro fiat, não pergunto se já passou na mão de assaltante.","created_at":1755213319,"id":"c8b875c54cf88745a7c75200b4d1f0a019561d289178dd1125e34c51a6e5b6d2","kind":1,"pubkey":"80c362d7c048f68f4e0991227e0c28f740c48fb95b5d8aac7233343671cef439","sig":"7cf9fe7908b9cae5ba4d44494874d34a59587b05b6e996cdf0f54a39fa36e08affbd69cbebea855910ea68b16fa2b2574fbd37a1790f5b7031195aaea5e7d124","tags":[["e","0a8093c0825679dcde7159a8af509b5ef818d5fc1049af582e012803d608a08f","","root"],["p","0153d742cf537c94e2bef9541cf3b02140a8a3b3641efe813d418451a2d44479"]]}]
|
||||
[19:15:31.497] SEND nostr.mom:443: ["CLOSE", "sync_0_1755213330"]
|
|
@ -1,85 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
|
||||
void hex_to_bytes(const char* hex_str, unsigned char* bytes) {
|
||||
size_t len = strlen(hex_str);
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
sscanf(hex_str + i, "%2hhx", &bytes[i / 2]);
|
||||
}
|
||||
}
|
||||
|
||||
int test_simple(void) {
|
||||
printf("=== SIMPLE TEST ===\n");
|
||||
|
||||
const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
|
||||
const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
|
||||
const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1";
|
||||
const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3";
|
||||
const char* plaintext = "test";
|
||||
|
||||
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
|
||||
hex_to_bytes(sk1_hex, sk1);
|
||||
hex_to_bytes(sk2_hex, sk2);
|
||||
hex_to_bytes(pk1_hex, pk1);
|
||||
hex_to_bytes(pk2_hex, pk2);
|
||||
|
||||
char encrypted[NOSTR_NIP04_MAX_ENCRYPTED_SIZE];
|
||||
int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, sizeof(encrypted));
|
||||
|
||||
if (result != NOSTR_SUCCESS) {
|
||||
printf("❌ ENCRYPTION FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("✅ Encryption: PASS\n");
|
||||
|
||||
char decrypted[NOSTR_NIP04_MAX_PLAINTEXT_SIZE];
|
||||
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, sizeof(decrypted));
|
||||
|
||||
if (result != NOSTR_SUCCESS) {
|
||||
printf("❌ DECRYPTION FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("✅ Decryption: PASS\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== DEBUG SEGFAULT TEST ===\n");
|
||||
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
printf("ERROR: Failed to initialize NOSTR library\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("✅ Library initialized\n");
|
||||
|
||||
// Test 1
|
||||
if (!test_simple()) {
|
||||
printf("❌ Test 1 FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("✅ Test 1 PASSED\n");
|
||||
|
||||
// Test 2 - same as test 1
|
||||
if (!test_simple()) {
|
||||
printf("❌ Test 2 FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("✅ Test 2 PASSED\n");
|
||||
|
||||
// Test 3 - same as test 1
|
||||
if (!test_simple()) {
|
||||
printf("❌ Test 3 FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("✅ Test 3 PASSED\n");
|
||||
|
||||
printf("✅ ALL TESTS PASSED - No segfault!\n");
|
||||
|
||||
nostr_cleanup();
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
|
||||
int main(void) {
|
||||
printf("Header included successfully\n");
|
||||
return 0;
|
||||
}
|
BIN
tests/http_test
BIN
tests/http_test
Binary file not shown.
Binary file not shown.
|
@ -1,6 +0,0 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello from minimal test\n");
|
||||
return 0;
|
||||
}
|
BIN
tests/nip05_test
BIN
tests/nip05_test
Binary file not shown.
|
@ -8,7 +8,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../nostr_core/nip005.h"
|
||||
|
||||
// Test helper function
|
||||
void print_test_result(const char* test_name, int result) {
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
#include <stdio.h>
|
||||
#include "../nostr_core/nostr_core.h"
|
||||
#include "../nostr_core/nip001.h"
|
||||
|
||||
int main(void) {
|
||||
printf("Testing basic library initialization...\n");
|
||||
|
|
Loading…
Reference in New Issue