|
#!/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 |