Skip to content

Instantly share code, notes, and snippets.

@xstelea
Last active May 26, 2025 22:28
Show Gist options
  • Select an option

  • Save xstelea/803396424b386613379b7617b9c2c9a5 to your computer and use it in GitHub Desktop.

Select an option

Save xstelea/803396424b386613379b7617b9c2c9a5 to your computer and use it in GitHub Desktop.
Hookah Transaction intent JSON-RPC API

E2E Test Scripts

This directory contains end-to-end test scripts for the Hookah JSON-RPC API.

JSON-RPC Test Script

The json-rpc-test.sh script replicates the functionality of the TypeScript e2e test, performing a complete authentication and transaction flow using bash and command-line tools.

Dependencies

The script requires the following command-line tools:

  • curl - for making HTTP requests
  • jq - for JSON parsing and manipulation
  • openssl - for cryptographic operations
  • xxd - for hex/binary conversion

Install dependencies on macOS:

brew install curl jq openssl
# xxd is included with vim, which is usually pre-installed

Install dependencies on Ubuntu/Debian:

sudo apt-get update
sudo apt-get install curl jq openssl vim-common

Usage

Basic usage:

./e2e/scripts/json-rpc-test.sh

For help:

./e2e/scripts/json-rpc-test.sh --help

Environment Configuration

Set the ENV environment variable to control which API endpoint to use:

For local testing:

ENV=local ./e2e/scripts/json-rpc-test.sh

For production testing (default):

ENV=production ./e2e/scripts/json-rpc-test.sh

What the Script Does

The script performs the following operations in sequence:

  1. Key Generation: Generates a random private/public key pair
  2. Virtual Account: Gets the virtual account address from the public key
  3. Authentication Challenge: Requests a ROLA authentication challenge
  4. Signature: Signs the challenge with the private key
  5. Login: Authenticates using the signed challenge
  6. Session Validation: Verifies the authentication session
  7. Transaction Creation: Creates a transaction intent to get XRD from the faucet
  8. Transaction Signing: Signs the transaction with the private key
  9. Transaction Submission: Submits the signed transaction

Output

The script provides colored, step-by-step output showing:

  • Progress through each phase
  • Generated keys and addresses
  • Authentication tokens and user IDs
  • Transaction details
  • Final summary with all important identifiers

Limitations

Important Note: The current implementation uses a simplified signature mechanism for demonstration purposes. In a production environment, you would need to use proper Ed25519 cryptographic libraries for accurate signature generation and public key derivation.

For production use, consider:

  • Using a proper Ed25519 implementation (e.g., via Node.js bindings)
  • Implementing proper key derivation from private to public keys
  • Adding proper error handling for cryptographic operations
  • Validating all responses more thoroughly

Example Output

[INFO] JSON-RPC E2E Test Script
[INFO] Environment: local
[INFO] Base URL: http://localhost:3000/api/jsonrpc

[INFO] Starting JSON-RPC e2e test
[INFO] Generating private key...
[INFO] Private key: a1b2c3d4e5f6...
[SUCCESS] Virtual account address: account_tdx_2_1abc123...
[SUCCESS] Challenge: xyz789...
[SUCCESS] Session validation successful
[SUCCESS] Transaction submitted successfully
[SUCCESS] JSON-RPC e2e test completed successfully!

=== Test Summary ===
Virtual Account: account_tdx_2_1abc123...
User ID: user_abc123
Transaction ID: txn_xyz789
Status: SUCCESS

Troubleshooting

  1. Missing Dependencies: The script will check for required tools and exit with an error if any are missing.

  2. Network Issues: If the API is unreachable, curl will fail with connection errors.

  3. Authentication Failures: If signature verification fails, check that the Ed25519 implementation is working correctly.

  4. JSON Parsing Errors: Ensure jq is properly installed and the API responses are valid JSON.

Development

To modify or extend the script:

  1. Edit json-rpc-test.sh
  2. Test with local environment first: ENV=local ./e2e/scripts/json-rpc-test.sh
  3. Ensure all error cases are handled appropriately
  4. Update this README if you add new functionality
#!/bin/bash
set -euo pipefail
# Configuration
ENV=${ENV:-"production"}
if [ "$ENV" = "local" ]; then
BASE_URL="http://localhost:3000/api/jsonrpc"
else
BASE_URL="https://app.hookah.ing/api/jsonrpc"
fi
NETWORK_ID=2
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check dependencies
check_dependencies() {
local deps=("curl" "jq" "node")
for dep in "${deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then
log_error "Required dependency '$dep' is not installed"
exit 1
fi
done
}
# Generate Ed25519 key pair using Node.js
generate_ed25519_keypair() {
node -e "
const crypto = require('crypto');
const { generateKeyPairSync } = crypto;
const { privateKey, publicKey } = generateKeyPairSync('ed25519', {
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
publicKeyEncoding: { type: 'spki', format: 'der' }
});
// Extract raw 32-byte keys from DER format
const privateKeyRaw = privateKey.slice(-32);
const publicKeyRaw = publicKey.slice(-32);
console.log(JSON.stringify({
privateKey: privateKeyRaw.toString('hex'),
publicKey: publicKeyRaw.toString('hex')
}));
"
}
# Sign message with Ed25519 using Node.js
sign_ed25519() {
local message_hex=$1
local private_key_hex=$2
node -e "
const crypto = require('crypto');
const messageHex = '$message_hex';
const privateKeyHex = '$private_key_hex';
// Convert hex to buffers
const message = Buffer.from(messageHex, 'hex');
const privateKeyRaw = Buffer.from(privateKeyHex, 'hex');
// Create private key object
const privateKeyDer = Buffer.concat([
Buffer.from('302e020100300506032b657004220420', 'hex'),
privateKeyRaw
]);
const privateKey = crypto.createPrivateKey({
key: privateKeyDer,
format: 'der',
type: 'pkcs8'
});
// Sign the message
const signature = crypto.sign(null, message, privateKey);
console.log(signature.toString('hex'));
"
}
# Make JSON-RPC request
make_rpc_request() {
local method=$1
local params=$2
local auth_header=$3
local request_body=$(jq -n \
--arg method "$method" \
--argjson params "$params" \
'{
jsonrpc: "2.0",
id: 1,
method: $method,
params: $params
}')
local curl_args=(-X POST -H "Content-Type: application/json" -d "$request_body" "$BASE_URL")
if [ -n "$auth_header" ]; then
curl_args+=(-H "Authorization: Bearer $auth_header")
fi
local response=$(curl -s "${curl_args[@]}")
# Check if response contains error
if echo "$response" | jq -e '.error' > /dev/null; then
log_error "RPC Error: $(echo "$response" | jq -r '.error.message')"
echo "$response" | jq '.'
exit 1
fi
echo "$response"
}
# Main test function
run_json_rpc_test() {
log_info "Starting JSON-RPC e2e test"
# Generate Ed25519 key pair
log_info "Generating Ed25519 key pair..."
local keypair_json=$(generate_ed25519_keypair)
local private_key_hex=$(echo "$keypair_json" | jq -r '.privateKey')
local public_key_hex=$(echo "$keypair_json" | jq -r '.publicKey')
log_info "Private key: ${private_key_hex:0:16}..."
log_info "Public key: ${public_key_hex:0:16}..."
# Step 1: Get virtual account address
log_info "Getting virtual account address..."
local virtual_account_params=$(jq -n \
--arg publicKey "$public_key_hex" \
--arg curve "curve25519" \
--argjson networkId $NETWORK_ID \
'{
publicKey: $publicKey,
curve: $curve,
networkId: $networkId
}')
local virtual_account_response=$(make_rpc_request "ret.virtualAccountAddressFromPublicKey" "$virtual_account_params" "")
local virtual_account_address=$(echo "$virtual_account_response" | jq -r '.result')
log_success "Virtual account address: $virtual_account_address"
# Step 2: Generate ROLA challenge
log_info "Generating ROLA challenge..."
local challenge_response=$(make_rpc_request "auth.generateRolaHash" "{}" "")
local challenge=$(echo "$challenge_response" | jq -r '.result.challenge')
local hash=$(echo "$challenge_response" | jq -r '.result.hash')
log_success "Challenge: ${challenge:0:32}..."
log_success "Hash: ${hash:0:32}..."
# Step 3: Sign the hash
log_info "Signing challenge hash..."
local signature=$(sign_ed25519 "$hash" "$private_key_hex")
log_success "Signature: ${signature:0:32}..."
# Step 4: Sign in with ROLA
log_info "Signing in with ROLA..."
local signin_params=$(jq -n \
--arg challenge "$challenge" \
--arg signature "$signature" \
--arg publicKey "$public_key_hex" \
--arg curve "curve25519" \
'{
challenge: $challenge,
signature: $signature,
publicKey: {
publicKey: $publicKey,
curve: $curve
}
}')
local signin_response=$(make_rpc_request "auth.signInWithRola" "$signin_params" "")
local token=$(echo "$signin_response" | jq -r '.result.token')
local user_id=$(echo "$signin_response" | jq -r '.result.session.userId')
log_success "Token: ${token:0:20}..."
log_success "User ID: $user_id"
# Step 5: Get session
log_info "Getting session..."
local session_response=$(make_rpc_request "auth.getSession" "{}" "$token")
local session_user_id=$(echo "$session_response" | jq -r '.result.session.userId')
if [ "$session_user_id" = "$user_id" ]; then
log_success "Session validation successful"
else
log_error "Session validation failed: expected $user_id, got $session_user_id"
exit 1
fi
# Step 6: Create transaction intent
log_info "Creating transaction intent..."
local manifest="CALL_METHOD
Address(\"component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxyulkzl\")
\"lock_fee\"
Decimal(\"10\")
;
CALL_METHOD
Address(\"component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxyulkzl\")
\"free\"
;
TAKE_ALL_FROM_WORKTOP
Address(\"resource_tdx_2_1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxtfd2jc\")
Bucket(\"bucket1\")
;
CALL_METHOD
Address(\"$virtual_account_address\")
\"deposit\"
Bucket(\"bucket1\")
;"
local transaction_params=$(jq -n \
--arg manifest "$manifest" \
--argjson networkId $NETWORK_ID \
'{
manifest: {
instructions: {
kind: "String",
value: $manifest
},
blobs: []
},
networkId: $networkId
}')
local transaction_response=$(make_rpc_request "transactionIntent.createTransactionIntent" "$transaction_params" "$token")
local transaction_id=$(echo "$transaction_response" | jq -r '.result.id')
local intent_hash=$(echo "$transaction_response" | jq -r '.result.intentHash')
log_success "Transaction ID: $transaction_id"
log_success "Intent hash: ${intent_hash:0:32}..."
# Step 7: Provide signature
log_info "Providing signature for transaction..."
local transaction_signature=$(sign_ed25519 "$intent_hash" "$private_key_hex")
local signature_params=$(jq -n \
--arg transactionId "$transaction_id" \
--arg signerPublicKey "$public_key_hex" \
--arg curve "EddsaEd25519" \
--arg signature "$transaction_signature" \
'{
transactionId: $transactionId,
signerPublicKey: $signerPublicKey,
curve: $curve,
signature: $signature
}')
local signature_response=$(make_rpc_request "transactionIntent.provideSignature" "$signature_params" "$token")
log_success "Signature provided successfully"
# Step 8: Submit transaction intent
log_info "Submitting transaction intent..."
local submit_params=$(jq -n \
--arg transactionId "$transaction_id" \
'{
transactionId: $transactionId
}')
local submit_response=$(make_rpc_request "transactionIntent.submitTransactionIntent" "$submit_params" "$token")
local submitted_transaction_id=$(echo "$submit_response" | jq -r '.result.transactionId // .result.id // "N/A"')
log_success "Transaction submitted successfully"
log_success "Submitted Transaction ID: $submitted_transaction_id"
log_success "JSON-RPC e2e test completed successfully!"
# Print final summary
echo
echo "=== Test Summary ==="
echo "Virtual Account: $virtual_account_address"
echo "User ID: $user_id"
echo "Transaction Intent ID: $transaction_id"
echo "Submitted Transaction ID: $submitted_transaction_id"
echo "Status: SUCCESS"
}
# Main execution
main() {
log_info "JSON-RPC E2E Test Script"
log_info "Environment: $ENV"
log_info "Base URL: $BASE_URL"
echo
check_dependencies
run_json_rpc_test
}
# Handle script arguments
case "${1:-}" in
--help|-h)
echo "Usage: $0 [options]"
echo
echo "Options:"
echo " --help, -h Show this help message"
echo
echo "Environment variables:"
echo " ENV Set to 'local' for local testing, anything else for production"
echo
echo "Dependencies: curl, jq, node"
exit 0
;;
*)
main "$@"
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment