Last version before deleting Makefile and CmakeLists.txt

This commit is contained in:
Laan Tungir 2025-08-15 16:32:59 -04:00
parent 6014a250dd
commit 8ed9262c65
79 changed files with 2908 additions and 502 deletions

View File

@ -14,10 +14,10 @@ ifneq ($(ENABLE_LOGGING),)
endif endif
# Include paths # 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 # Library source files - modular NIP-based structure
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 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) LIB_OBJECTS = $(LIB_SOURCES:.c=.o)
ARM64_LIB_OBJECTS = $(LIB_SOURCES:.c=.arm64.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_RELAY_POOL_TEST_EXEC = tests/relay_pool_test_arm64
ARM64_NIP04_TEST_EXEC = tests/nip04_test_arm64 ARM64_NIP04_TEST_EXEC = tests/nip04_test_arm64
# Test compilation flags # Test compilation flags - matches exact customer usage pattern
TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include -DDISABLE_NIP05 TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include
TEST_LDFLAGS = -L. -lnostr_core -lm -static TEST_LDFLAGS = ./libnostr_core.a -lm -static
ARM64_TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -DDISABLE_NIP05 ARM64_TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I.
ARM64_TEST_LDFLAGS = -L. -lnostr_core_arm64 -lssl -lcrypto -lm -static ARM64_TEST_LDFLAGS = ./libnostr_core_arm64.a -lm -static
# Build crypto test executable (x86_64) # Build crypto test executable (x86_64)
$(CRYPTO_TEST_EXEC): tests/nostr_crypto_test.c $(STATIC_LIB) $(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)..." @echo "Building NIP-04 encryption test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS) $(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build HTTP test executable (x86_64) - Uses static curl for compatibility testing # Build HTTP test executable (x86_64) - Uses customer linking pattern
$(HTTP_TEST_EXEC): tests/http_test.c $(HTTP_TEST_EXEC): tests/http_test.c $(STATIC_LIB)
@echo "Building HTTP/curl compatibility test (x86_64)..." @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) # Build WebSocket SSL test executable (x86_64)
$(WSS_TEST_EXEC): tests/wss_test.c $(STATIC_LIB) $(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)..." @echo "Building Makefile-based static configuration test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ -static $(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) $(NIP05_TEST_EXEC): tests/nip05_test.c $(STATIC_LIB)
@echo "Building NIP-05 identifier verification test (x86_64)..." @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) $(NIP11_TEST_EXEC): tests/nip11_test.c $(STATIC_LIB)
@echo "Building NIP-11 relay information test (x86_64)..." @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) # Build simple initialization test executable (x86_64)
tests/simple_init_test: tests/simple_init_test.c $(STATIC_LIB) tests/simple_init_test: tests/simple_init_test.c $(STATIC_LIB)

692
build.sh
View File

@ -1,10 +1,9 @@
#!/bin/bash #!/bin/bash
# NOSTR Core Library Build Script # NOSTR Core Library - Customer Build Script with Auto-Detection
# Provides convenient build targets for the standalone library # Automatically detects which NIPs are needed based on #include statements
# Automatically increments patch version with each build
set -e # Exit on any error set -e # Exit on error
# Colors for output # Colors for output
RED='\033[0;31m' RED='\033[0;31m'
@ -14,7 +13,7 @@ BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Function to print colored output # Function to print colored output
print_status() { print_info() {
echo -e "${BLUE}[INFO]${NC} $1" echo -e "${BLUE}[INFO]${NC} $1"
} }
@ -30,365 +29,374 @@ print_error() {
echo -e "${RED}[ERROR]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# Function to automatically increment version # Default values
increment_version() { ARCHITECTURE=""
print_status "Incrementing version..." FORCE_NIPS=""
VERBOSE=false
# Check if we're in a git repository HELP=false
if ! git rev-parse --git-dir > /dev/null 2>&1; then BUILD_TESTS=false
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 # Parse command line arguments
TARGET=""
COMMIT_MESSAGE=""
# Parse arguments
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
-m) x64|x86_64|amd64)
COMMIT_MESSAGE="$2" ARCHITECTURE="x64"
shift 2 shift
;; ;;
-*) arm64|aarch64)
print_error "Unknown option: $1" ARCHITECTURE="arm64"
show_usage shift
exit 1 ;;
--nips=*)
FORCE_NIPS="${1#*=}"
shift
;;
--verbose|-v)
VERBOSE=true
shift
;;
--tests)
BUILD_TESTS=true
shift
;;
--help|-h)
HELP=true
shift
;; ;;
*) *)
if [[ -z "$TARGET" ]]; then print_error "Unknown argument: $1"
TARGET="$1" HELP=true
else
print_error "Multiple targets specified: $TARGET and $1"
show_usage
exit 1
fi
shift shift
;; ;;
esac esac
done done
# Set default target if none specified # Show help
TARGET=${TARGET:-lib} 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 print_info "NOSTR Core Library - Customer Build Script"
clean) print_info "Auto-detecting needed NIPs from your source code..."
print_status "Cleaning build artifacts..."
make clean
print_success "Clean completed"
;;
lib|library) # Detect NIPs from source files
increment_version NEEDED_NIPS=""
print_status "Building both x64 and ARM64 static libraries..." if [ -n "$FORCE_NIPS" ]; then
make clean if [ "$FORCE_NIPS" = "all" ]; then
make 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 # Check for nostr_core.h (includes everything)
SUCCESS=0 if grep -q '#include[[:space:]]*["\<]nostr_core\.h["\>]' *.c 2>/dev/null; then
if [ -f "libnostr_core.a" ]; then print_info "Found #include \"nostr_core.h\" - building all NIPs"
SIZE_X64=$(stat -c%s "libnostr_core.a") NEEDED_NIPS="001 005 006 011 013 019 044"
print_success "x64 static library built successfully (${SIZE_X64} bytes)" elif [ -n "$DETECTED" ]; then
SUCCESS=$((SUCCESS + 1)) NEEDED_NIPS="$DETECTED"
print_success "Auto-detected NIPs: $(echo $NEEDED_NIPS | tr ' ' ',')"
else 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 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 # Ensure NIP-001 is always included (required for core functionality)
SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a") if ! echo "$NEEDED_NIPS" | grep -q "001"; then
print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)" NEEDED_NIPS="001 $NEEDED_NIPS"
SUCCESS=$((SUCCESS + 1)) print_info "Added NIP-001 (required for core functionality)"
else fi
print_error "Failed to build ARM64 static library"
fi
if [ $SUCCESS -eq 2 ]; then # Determine architecture
print_success "Both architectures built successfully!" if [ -z "$ARCHITECTURE" ]; then
ls -la libnostr_core*.a ARCH=$(uname -m)
perform_git_operations "$COMMIT_MESSAGE" case $ARCH in
else x86_64|amd64)
print_error "Failed to build all libraries" ARCHITECTURE="x64"
exit 1 ;;
fi 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"
;; ;;
arm64)
x64|x64-only) CC="aarch64-linux-gnu-gcc"
increment_version ARCH_SUFFIX="arm64"
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" print_error "Unsupported architecture: $ARCHITECTURE"
echo ""
show_usage
exit 1 exit 1
;; ;;
esac 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

394
dev_build.sh Executable file
View File

@ -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

40
example_basic.c Normal file
View File

@ -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;
}

104
example_modular_nips.c Normal file
View File

@ -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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,7 @@
*/ */
#include "nostr_crypto.h" #include "nostr_crypto.h"
#include "nostr_core.h" // For error constants #include "../nostr_common.h" // For error constants
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>

172
nostr_core/nip001.c Normal file
View File

@ -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;
}

36
nostr_core/nip001.h Normal file
View File

@ -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

388
nostr_core/nip005.c Normal file
View File

@ -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

18
nostr_core/nip005.h Normal file
View File

@ -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

118
nostr_core/nip006.c Normal file
View File

@ -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

30
nostr_core/nip006.h Normal file
View File

@ -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

473
nostr_core/nip011.c Normal file
View File

@ -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

84
nostr_core/nip011.h Normal file
View File

@ -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

278
nostr_core/nip013.c Normal file
View File

@ -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;
}

18
nostr_core/nip013.h Normal file
View File

@ -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

264
nostr_core/nip019.c Normal file
View File

@ -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;
}

17
nostr_core/nip019.h Normal file
View File

@ -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.

46
nostr_core/nostr_common.h Normal file
View File

@ -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.

36
nostr_core/utils.c Normal file
View File

@ -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
}

20
nostr_core/utils.h Normal file
View File

@ -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

View File

@ -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"]

View File

@ -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.

View File

@ -1,7 +0,0 @@
#include <stdio.h>
#include "../nostr_core/nostr_core.h"
int main(void) {
printf("Header included successfully\n");
return 0;
}

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +0,0 @@
#include <stdio.h>
int main(void) {
printf("Hello from minimal test\n");
return 0;
}

Binary file not shown.

View File

@ -8,7 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "../nostr_core/nostr_core.h" #include "../nostr_core/nip005.h"
// Test helper function // Test helper function
void print_test_result(const char* test_name, int result) { void print_test_result(const char* test_name, int result) {

BIN
tests/old/http_test Executable file

Binary file not shown.

View File

@ -1,5 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include "../nostr_core/nostr_core.h" #include "../nostr_core/nip001.h"
int main(void) { int main(void) {
printf("Testing basic library initialization...\n"); printf("Testing basic library initialization...\n");