diff --git a/Dockerfile b/Dockerfile index d21668c..ce82d84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,28 +8,16 @@ RUN go mod download COPY . ./ RUN go build -o /bin/wallet-backend -ldflags "-X main.GitCommit=$GIT_COMMIT" . +# Use the official stellar/soroban-rpc image as the base +FROM stellar/soroban-rpc + +# Install bash or sh +RUN apt-get update && apt-get install -y bash # Step 2: Install Stellar Core and copy over app binary FROM ubuntu:jammy AS core-build -ARG STELLAR_CORE_VERSION - -RUN apt-get update && \ - apt-get install -y --no-install-recommends ca-certificates curl wget gnupg apt-utils gpg && \ - curl -sSL https://apt.stellar.org/SDF.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/SDF.gpg && \ - echo "deb https://apt.stellar.org jammy stable" >/etc/apt/sources.list.d/SDF.list && \ - echo "deb https://apt.stellar.org jammy testing" >/etc/apt/sources.list.d/SDF-testing.list && \ - echo "deb https://apt.stellar.org jammy unstable" >/etc/apt/sources.list.d/SDF-unstable.list && \ - apt-get update && \ - apt-get install -y stellar-core=${STELLAR_CORE_VERSION} && \ - apt-get clean - COPY --from=api-build /bin/wallet-backend /app/ -COPY --from=api-build /src/wallet-backend/internal/ingest/config/stellar-core_pubnet.cfg /app/config/stellar-core_pubnet.cfg -COPY --from=api-build /src/wallet-backend/internal/ingest/config/stellar-core_testnet.cfg /app/config/stellar-core_testnet.cfg - -ENV CAPTIVE_CORE_BIN_PATH /usr/bin/stellar-core -ENV CAPTIVE_CORE_CONFIG_DIR /app/config/ EXPOSE 8001 WORKDIR /app diff --git a/cmd/utils/global_options.go b/cmd/utils/global_options.go index 86e6847..1b719b8 100644 --- a/cmd/utils/global_options.go +++ b/cmd/utils/global_options.go @@ -73,7 +73,7 @@ func RPCURLOption(configKey *string) *config.ConfigOption { Usage: "The URL of the RPC Server.", OptType: types.String, ConfigKey: configKey, - FlagDefault: "localhost:8080", + FlagDefault: "http://soroban-rpc:8000", Required: true, } } diff --git a/config/soroban-rpc-pubnet-config.toml b/config/soroban-rpc-pubnet-config.toml new file mode 100644 index 0000000..e6ee38d --- /dev/null +++ b/config/soroban-rpc-pubnet-config.toml @@ -0,0 +1,213 @@ + +# Admin endpoint to listen and serve on. WARNING: this should not be accessible +# from the Internet and does not use TLS. "" (default) disables the admin server + ADMIN_ENDPOINT = "0.0.0.0:8001" + +# path to additional configuration for the Stellar Core configuration file used +# by captive core. It must, at least, include enough details to define a quorum +# set +CAPTIVE_CORE_CONFIG_PATH = "/config/stellar-core_pubnet.cfg" + +# Storage location for Captive Core bucket data +CAPTIVE_CORE_STORAGE_PATH = "/" + +# establishes how many ledgers exist between checkpoints, do NOT change this +# unless you really know what you are doing +CHECKPOINT_FREQUENCY = 64 + +# configures classic fee stats retention window expressed in number of ledgers +CLASSIC_FEE_STATS_RETENTION_WINDOW = 10 + +# SQLite DB path +DB_PATH = "soroban_rpc.sqlite" + +# Default cap on the amount of events included in a single getEvents response +DEFAULT_EVENTS_LIMIT = 100 + +# Default cap on the amount of transactions included in a single getTransactions +# response +DEFAULT_TRANSACTIONS_LIMIT = 200 + +# Endpoint to listen and serve on +ENDPOINT = "0.0.0.0:8000" + +# (Deprecated, overidden by history-retention-window) configures the event +# retention window expressed in number of ledgers, the default value is 17280 +# which corresponds to about 24 hours of history +EVENT_RETENTION_WINDOW = 17280 + +# The friendbot URL to be returned by getNetwork endpoint +# FRIENDBOT_URL = "" + +# comma-separated list of stellar history archives to connect with +HISTORY_ARCHIVE_URLS = ["https://history.stellar.org/prd/core-live/core_live_001/", "https://history.stellar.org/prd/core-live/core_live_002/", "https://history.stellar.org/prd/core-live/core_live_003/"] + + +# configures history retention window for transactions and events, expressed in +# number of ledgers, the default value is 17280 which corresponds to about 24 +# hours of history +HISTORY_RETENTION_WINDOW = 17280 + +# Ingestion Timeout when bootstrapping data (checkpoint and in-memory +# initialization) and preparing ledger reads +INGESTION_TIMEOUT = "50m0s" + +# format used for output logs (json or text) +# LOG_FORMAT = "text" + +# minimum log severity (debug, info, warn, error) to log +LOG_LEVEL = "info" + +# Maximum amount of events allowed in a single getEvents response +MAX_EVENTS_LIMIT = 10000 + +# The maximum duration of time allowed for processing a getEvents request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_EVENTS_EXECUTION_DURATION = "10s" + +# The maximum duration of time allowed for processing a getFeeStats request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_FEE_STATS_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getHealth request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_HEALTH_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getLatestLedger request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_LATEST_LEDGER_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getLedgerEntries +# request. When that time elapses, the rpc server would return -32001 and abort +# the request's execution +MAX_GET_LEDGER_ENTRIES_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getNetwork request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_NETWORK_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getTransactions request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_TRANSACTIONS_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getTransaction request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_TRANSACTION_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getVersionInfo request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_VERSION_INFO_EXECUTION_DURATION = "5s" + +# maximum ledger latency (i.e. time elapsed since the last known ledger closing +# time) considered to be healthy (used for the /health endpoint) +MAX_HEALTHY_LEDGER_LATENCY = "30s" + +# The max request execution duration is the predefined maximum duration of time +# allowed for processing a request. When that time elapses, the server would +# return 504 and abort the request's execution +MAX_REQUEST_EXECUTION_DURATION = "25s" + +# The maximum duration of time allowed for processing a sendTransaction request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_SEND_TRANSACTION_EXECUTION_DURATION = "15s" + +# The maximum duration of time allowed for processing a simulateTransaction +# request. When that time elapses, the rpc server would return -32001 and abort +# the request's execution +MAX_SIMULATE_TRANSACTION_EXECUTION_DURATION = "15s" + +# Maximum amount of transactions allowed in a single getTransactions response +MAX_TRANSACTIONS_LIMIT = 200 + +# Network passphrase of the Stellar network transactions should be signed for. +# Commonly used values are "Test SDF Future Network ; October 2022", "Test SDF +# Network ; September 2015" and "Public Global Stellar Network ; September 2015" +NETWORK_PASSPHRASE = "Public Global Stellar Network ; September 2015" + +# Enable debug information in preflighting (provides more detailed errors). It +# should not be enabled in production deployments. +PREFLIGHT_ENABLE_DEBUG = true + +# Number of workers (read goroutines) used to compute preflights for the +# simulateTransaction endpoint. Defaults to the number of CPUs. +PREFLIGHT_WORKER_COUNT = 8 + +# Maximum number of outstanding preflight requests for the simulateTransaction +# endpoint. Defaults to the number of CPUs. +PREFLIGHT_WORKER_QUEUE_SIZE = 8 + +# Maximum number of outstanding GetEvents requests +REQUEST_BACKLOG_GET_EVENTS_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetFeeStats requests +REQUEST_BACKLOG_GET_FEE_STATS_QUEUE_LIMIT = 100 + +# Maximum number of outstanding GetHealth requests +REQUEST_BACKLOG_GET_HEALTH_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetLatestsLedger requests +REQUEST_BACKLOG_GET_LATEST_LEDGER_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetLedgerEntries requests +REQUEST_BACKLOG_GET_LEDGER_ENTRIES_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetNetwork requests +REQUEST_BACKLOG_GET_NETWORK_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetTransactions requests +REQUEST_BACKLOG_GET_TRANSACTIONS_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetTransaction requests +REQUEST_BACKLOG_GET_TRANSACTION_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetVersionInfo requests +REQUEST_BACKLOG_GET_VERSION_INFO_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding requests +REQUEST_BACKLOG_GLOBAL_QUEUE_LIMIT = 5000 + +# Maximum number of outstanding SendTransaction requests +REQUEST_BACKLOG_SEND_TRANSACTION_QUEUE_LIMIT = 500 + +# Maximum number of outstanding SimulateTransaction requests +REQUEST_BACKLOG_SIMULATE_TRANSACTION_QUEUE_LIMIT = 100 + +# The request execution warning threshold is the predetermined maximum duration +# of time that a request can take to be processed before a warning would be +# generated +REQUEST_EXECUTION_WARNING_THRESHOLD = "5s" + +# configures soroban inclusion fee stats retention window expressed in number of +# ledgers +SOROBAN_FEE_STATS_RETENTION_WINDOW = 50 + +# HTTP port for Captive Core to listen on (0 disables the HTTP server) +STELLAR_CAPTIVE_CORE_HTTP_PORT = 11626 + +# path to stellar core binary +STELLAR_CORE_BINARY_PATH = "/usr/bin/stellar-core" + +# Timeout used when submitting requests to stellar-core +STELLAR_CORE_TIMEOUT = "2s" + +# URL used to query Stellar Core (local captive core by default) +# STELLAR_CORE_URL = "" + +# Enable strict toml configuration file parsing. This will prevent unknown +# fields in the config toml from being parsed. +# STRICT = false + +# (Deprecated, overidden by history-retention-window) configures the transaction +# retention window expressed in number of ledgers, the default value is 17280 +# which corresponds to about 24 hours of history +TRANSACTION_RETENTION_WINDOW = 17280 + diff --git a/config/soroban-rpc-testnet-config.toml b/config/soroban-rpc-testnet-config.toml new file mode 100644 index 0000000..22f42bc --- /dev/null +++ b/config/soroban-rpc-testnet-config.toml @@ -0,0 +1,212 @@ + +# Admin endpoint to listen and serve on. WARNING: this should not be accessible +# from the Internet and does not use TLS. "" (default) disables the admin server + ADMIN_ENDPOINT = "0.0.0.0:8001" + +# path to additional configuration for the Stellar Core configuration file used +# by captive core. It must, at least, include enough details to define a quorum +# set +CAPTIVE_CORE_CONFIG_PATH = "/config/stellar-core_testnet.cfg" + +# Storage location for Captive Core bucket data +CAPTIVE_CORE_STORAGE_PATH = "/" + +# establishes how many ledgers exist between checkpoints, do NOT change this +# unless you really know what you are doing +CHECKPOINT_FREQUENCY = 64 + +# configures classic fee stats retention window expressed in number of ledgers +CLASSIC_FEE_STATS_RETENTION_WINDOW = 10 + +# SQLite DB path +DB_PATH = "soroban_rpc.sqlite" + +# Default cap on the amount of events included in a single getEvents response +DEFAULT_EVENTS_LIMIT = 100 + +# Default cap on the amount of transactions included in a single getTransactions +# response +DEFAULT_TRANSACTIONS_LIMIT = 200 + +# Endpoint to listen and serve on +ENDPOINT = "0.0.0.0:8000" + +# (Deprecated, overidden by history-retention-window) configures the event +# retention window expressed in number of ledgers, the default value is 17280 +# which corresponds to about 24 hours of history +EVENT_RETENTION_WINDOW = 17280 + +# The friendbot URL to be returned by getNetwork endpoint +# FRIENDBOT_URL = "" + +# comma-separated list of stellar history archives to connect with +HISTORY_ARCHIVE_URLS = ["https://history.stellar.org/prd/core-testnet/core_testnet_001/", "https://history.stellar.org/prd/core-testnet/core_testnet_002/", "https://history.stellar.org/prd/core-testnet/core_testnet_003"] + +# configures history retention window for transactions and events, expressed in +# number of ledgers, the default value is 17280 which corresponds to about 24 +# hours of history +HISTORY_RETENTION_WINDOW = 17280 + +# Ingestion Timeout when bootstrapping data (checkpoint and in-memory +# initialization) and preparing ledger reads +INGESTION_TIMEOUT = "50m0s" + +# format used for output logs (json or text) +# LOG_FORMAT = "text" + +# minimum log severity (debug, info, warn, error) to log +LOG_LEVEL = "info" + +# Maximum amount of events allowed in a single getEvents response +MAX_EVENTS_LIMIT = 10000 + +# The maximum duration of time allowed for processing a getEvents request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_EVENTS_EXECUTION_DURATION = "10s" + +# The maximum duration of time allowed for processing a getFeeStats request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_FEE_STATS_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getHealth request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_HEALTH_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getLatestLedger request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_LATEST_LEDGER_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getLedgerEntries +# request. When that time elapses, the rpc server would return -32001 and abort +# the request's execution +MAX_GET_LEDGER_ENTRIES_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getNetwork request. When +# that time elapses, the rpc server would return -32001 and abort the request's +# execution +MAX_GET_NETWORK_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getTransactions request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_TRANSACTIONS_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getTransaction request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_TRANSACTION_EXECUTION_DURATION = "5s" + +# The maximum duration of time allowed for processing a getVersionInfo request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_GET_VERSION_INFO_EXECUTION_DURATION = "5s" + +# maximum ledger latency (i.e. time elapsed since the last known ledger closing +# time) considered to be healthy (used for the /health endpoint) +MAX_HEALTHY_LEDGER_LATENCY = "30s" + +# The max request execution duration is the predefined maximum duration of time +# allowed for processing a request. When that time elapses, the server would +# return 504 and abort the request's execution +MAX_REQUEST_EXECUTION_DURATION = "25s" + +# The maximum duration of time allowed for processing a sendTransaction request. +# When that time elapses, the rpc server would return -32001 and abort the +# request's execution +MAX_SEND_TRANSACTION_EXECUTION_DURATION = "15s" + +# The maximum duration of time allowed for processing a simulateTransaction +# request. When that time elapses, the rpc server would return -32001 and abort +# the request's execution +MAX_SIMULATE_TRANSACTION_EXECUTION_DURATION = "15s" + +# Maximum amount of transactions allowed in a single getTransactions response +MAX_TRANSACTIONS_LIMIT = 200 + +# Network passphrase of the Stellar network transactions should be signed for. +# Commonly used values are "Test SDF Future Network ; October 2022", "Test SDF +# Network ; September 2015" and "Public Global Stellar Network ; September 2015" +NETWORK_PASSPHRASE = "Test SDF Network ; September 2015" + +# Enable debug information in preflighting (provides more detailed errors). It +# should not be enabled in production deployments. +PREFLIGHT_ENABLE_DEBUG = true + +# Number of workers (read goroutines) used to compute preflights for the +# simulateTransaction endpoint. Defaults to the number of CPUs. +PREFLIGHT_WORKER_COUNT = 8 + +# Maximum number of outstanding preflight requests for the simulateTransaction +# endpoint. Defaults to the number of CPUs. +PREFLIGHT_WORKER_QUEUE_SIZE = 8 + +# Maximum number of outstanding GetEvents requests +REQUEST_BACKLOG_GET_EVENTS_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetFeeStats requests +REQUEST_BACKLOG_GET_FEE_STATS_QUEUE_LIMIT = 100 + +# Maximum number of outstanding GetHealth requests +REQUEST_BACKLOG_GET_HEALTH_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetLatestsLedger requests +REQUEST_BACKLOG_GET_LATEST_LEDGER_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetLedgerEntries requests +REQUEST_BACKLOG_GET_LEDGER_ENTRIES_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetNetwork requests +REQUEST_BACKLOG_GET_NETWORK_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetTransactions requests +REQUEST_BACKLOG_GET_TRANSACTIONS_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetTransaction requests +REQUEST_BACKLOG_GET_TRANSACTION_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding GetVersionInfo requests +REQUEST_BACKLOG_GET_VERSION_INFO_QUEUE_LIMIT = 1000 + +# Maximum number of outstanding requests +REQUEST_BACKLOG_GLOBAL_QUEUE_LIMIT = 5000 + +# Maximum number of outstanding SendTransaction requests +REQUEST_BACKLOG_SEND_TRANSACTION_QUEUE_LIMIT = 500 + +# Maximum number of outstanding SimulateTransaction requests +REQUEST_BACKLOG_SIMULATE_TRANSACTION_QUEUE_LIMIT = 100 + +# The request execution warning threshold is the predetermined maximum duration +# of time that a request can take to be processed before a warning would be +# generated +REQUEST_EXECUTION_WARNING_THRESHOLD = "5s" + +# configures soroban inclusion fee stats retention window expressed in number of +# ledgers +SOROBAN_FEE_STATS_RETENTION_WINDOW = 50 + +# HTTP port for Captive Core to listen on (0 disables the HTTP server) +STELLAR_CAPTIVE_CORE_HTTP_PORT = 11626 + +# path to stellar core binary +STELLAR_CORE_BINARY_PATH = "/usr/bin/stellar-core" + +# Timeout used when submitting requests to stellar-core +STELLAR_CORE_TIMEOUT = "2s" + +# URL used to query Stellar Core (local captive core by default) +# STELLAR_CORE_URL = "" + +# Enable strict toml configuration file parsing. This will prevent unknown +# fields in the config toml from being parsed. +# STRICT = false + +# (Deprecated, overidden by history-retention-window) configures the transaction +# retention window expressed in number of ledgers, the default value is 17280 +# which corresponds to about 24 hours of history +TRANSACTION_RETENTION_WINDOW = 17280 + diff --git a/config/soroban_rpc_startup.sh b/config/soroban_rpc_startup.sh new file mode 100644 index 0000000..9844c40 --- /dev/null +++ b/config/soroban_rpc_startup.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# Handle environment logic for Soroban RPC +if [ "$NETWORK" = "testnet" ]; then + CONFIG_FILE="/config/soroban-rpc-testnet-config.toml" +elif [ "$NETWORK" = "pubnet" ]; then + CONFIG_FILE="/config/soroban-rpc-pubnet-config.toml" +else + echo "Unknown environment: $NETWORK" + exit 1 +fi + +# Start Soroban RPC +exec /usr/bin/stellar-soroban-rpc --config-path "$CONFIG_FILE" diff --git a/config/stellar-core_pubnet.cfg b/config/stellar-core_pubnet.cfg new file mode 100644 index 0000000..8394e87 --- /dev/null +++ b/config/stellar-core_pubnet.cfg @@ -0,0 +1,25 @@ +# Stellar Pubnet validators +[[HOME_DOMAINS]] +HOME_DOMAIN="www.stellar.org" +QUALITY="HIGH" + +[[VALIDATORS]] +NAME="SDF 1" +PUBLIC_KEY="GCGB2S2KGYARPVIA37HYZXVRM2YZUEXA6S33ZU5BUDC6THSB62LZSTYH" +ADDRESS="core-live-a.stellar.org:11625" +HISTORY="curl -sf http://history.stellar.org/prd/core-live/core_live_001/{0} -o {1}" +HOME_DOMAIN="www.stellar.org" + +[[VALIDATORS]] +NAME="SDF 2" +PUBLIC_KEY="GCM6QMP3DLRPTAZW2UZPCPX2LF3SXWXKPMP3GKFZBDSF3QZGV2G5QSTK" +ADDRESS="core-live-b.stellar.org:11625" +HISTORY="curl -sf http://history.stellar.org/prd/core-live/core_live_002/{0} -o {1}" +HOME_DOMAIN="www.stellar.org" + +[[VALIDATORS]] +NAME="SDF 3" +PUBLIC_KEY="GABMKJM6I25XI4K7U6XWMULOUQIQ27BCTMLS6BYYSOWKTBUXVRJSXHYQ" +ADDRESS="core-live-c.stellar.org:11625" +HISTORY="curl -sf http://history.stellar.org/prd/core-live/core_live_003/{0} -o {1}" +HOME_DOMAIN="www.stellar.org" \ No newline at end of file diff --git a/config/stellar-core_testnet.cfg b/config/stellar-core_testnet.cfg new file mode 100644 index 0000000..357470a --- /dev/null +++ b/config/stellar-core_testnet.cfg @@ -0,0 +1,25 @@ +# Stellar Testnet validators +[[HOME_DOMAINS]] +HOME_DOMAIN="testnet.stellar.org" +QUALITY="HIGH" + +[[VALIDATORS]] +NAME="sdftest1" +HOME_DOMAIN="testnet.stellar.org" +PUBLIC_KEY="GDKXE2OZMJIPOSLNA6N6F2BVCI3O777I2OOC4BV7VOYUEHYX7RTRYA7Y" +ADDRESS="core-testnet1.stellar.org" +HISTORY="curl -sf http://history.stellar.org/prd/core-testnet/core_testnet_001/{0} -o {1}" + +[[VALIDATORS]] +NAME="sdftest2" +HOME_DOMAIN="testnet.stellar.org" +PUBLIC_KEY="GCUCJTIYXSOXKBSNFGNFWW5MUQ54HKRPGJUTQFJ5RQXZXNOLNXYDHRAP" +ADDRESS="core-testnet2.stellar.org" +HISTORY="curl -sf http://history.stellar.org/prd/core-testnet/core_testnet_002/{0} -o {1}" + +[[VALIDATORS]] +NAME="sdftest3" +HOME_DOMAIN="testnet.stellar.org" +PUBLIC_KEY="GC2V2EFSXN6SQTWVYA5EPJPBWWIMSD2XQNKUOHGEKB535AQE2I6IXV2Z" +ADDRESS="core-testnet3.stellar.org" +HISTORY="curl -sf http://history.stellar.org/prd/core-testnet/core_testnet_003/{0} -o {1}" \ No newline at end of file diff --git a/docker-compose-tmp.yaml b/docker-compose-tmp.yaml new file mode 100644 index 0000000..0f05137 --- /dev/null +++ b/docker-compose-tmp.yaml @@ -0,0 +1,72 @@ +services: + db: + image: postgres:12-alpine + environment: + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_DB: wallet-backend + volumes: + - postgres-db:/var/lib/postgresql/data + ports: + - 5432:5432 + api: + image: stellar/wallet-backend:development + build: + context: ./ + platforms: + - "linux/amd64" + #args: + # STELLAR_CORE_VERSION: 21.0.0-1872.c6f474133.jammy + depends_on: + db: + condition: service_started + ports: + - 8001:8001 + entrypoint: "" + command: + - sh + - -c + - | + ./wallet-backend migrate up + ./wallet-backend channel-account ensure 5 + ./wallet-backend serve + environment: + DATABASE_URL: postgres://postgres@db:5432/wallet-backend?sslmode=disable + PORT: 8001 + SERVER_BASE_URL: http://localhost:8001 + LOG_LEVEL: TRACE + WALLET_SIGNING_KEY: ${WALLET_SIGNING_KEY} + DISTRIBUTION_ACCOUNT_PUBLIC_KEY: ${DISTRIBUTION_ACCOUNT_PUBLIC_KEY} + DISTRIBUTION_ACCOUNT_SIGNATURE_PROVIDER: ${DISTRIBUTION_ACCOUNT_SIGNATURE_PROVIDER} + + # Env Signature Client + DISTRIBUTION_ACCOUNT_PRIVATE_KEY: ${DISTRIBUTION_ACCOUNT_PRIVATE_KEY} + + # KMS Signature Client + KMS_KEY_ARN: ${KMS_KEY_ARN} + AWS_REGIONG: ${AWS_REGION} + # Using KMS locally is necessary to inject the AWS credentials envs. + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} + + # Channel Account + CHANNEL_ACCOUNT_ENCRYPTION_PASSPHRASE: ${CHANNEL_ACCOUNT_ENCRYPTION_PASSPHRASE} + TRACKER_DSN: ${TRACKER_DSN} + STELLAR_ENVIRONMENT: ${STELLAR_ENVIRONMENT} + ingest: + image: stellar/wallet-backend:development + depends_on: + db: + condition: service_started + entrypoint: "" + command: + - sh + - -c + - ./wallet-backend ingest + environment: + DATABASE_URL: postgres://postgres@db:5432/wallet-backend?sslmode=disable + TRACKER_DSN: ${TRACKER_DSN} + STELLAR_ENVIRONMENT: ${STELLAR_ENVIRONMENT} +volumes: + postgres-db: + driver: local diff --git a/docker-compose.yaml b/docker-compose.yaml index 094f447..4d9b059 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,17 +8,26 @@ services: - postgres-db:/var/lib/postgresql/data ports: - 5432:5432 + soroban-rpc: + image: stellar/soroban-rpc + ports: + - 8000:8000 + volumes: + - ./config:/config + environment: + NETWORK: ${NETWORK} + entrypoint: ["/bin/bash", "/config/soroban_rpc_startup.sh"] api: image: stellar/wallet-backend:development build: context: ./ platforms: - "linux/amd64" - args: - STELLAR_CORE_VERSION: 21.0.0-1872.c6f474133.jammy depends_on: db: condition: service_started + soroban-rpc: + condition: service_started ports: - 8001:8001 entrypoint: "" @@ -58,6 +67,8 @@ services: depends_on: db: condition: service_started + soroban-rpc: + condition: service_started entrypoint: "" command: - sh diff --git a/internal/services/ingest.go b/internal/services/ingest.go index 43ccf30..cae959d 100644 --- a/internal/services/ingest.go +++ b/internal/services/ingest.go @@ -86,17 +86,20 @@ func (m *ingestService) Run(ctx context.Context, startLedger uint32, endLedger u time.Sleep(10 * time.Second) ledgerTransactions, err := m.GetLedgerTransactions(int64(ingestLedger)) if err != nil { - return fmt.Errorf("getTransactions: %w", err) + log.Error("getTransactions: %w", err) + continue } heartbeat <- true err = m.ingestPayments(ctx, ledgerTransactions) if err != nil { return fmt.Errorf("error ingesting payments: %w", err) } + err = m.processTSSTransactions(ctx, ledgerTransactions) if err != nil { return fmt.Errorf("error processing tss transactions: %w", err) } + err = m.models.Payments.UpdateLatestLedgerSynced(ctx, m.ledgerCursorName, uint32(ingestLedger)) if err != nil { return fmt.Errorf("error updating latest synced ledger: %w", err) @@ -154,7 +157,6 @@ func (m *ingestService) ingestPayments(ctx context.Context, ledgerTransactions [ } for idx, op := range txEnvelopeXDR.Operations() { opIdx := idx + 1 - payment := data.Payment{ OperationID: utils.OperationID(int32(tx.Ledger), int32(tx.ApplicationOrder), int32(opIdx)), OperationType: op.Body.Type.String(), @@ -165,7 +167,6 @@ func (m *ingestService) ingestPayments(ctx context.Context, ledgerTransactions [ Memo: txMemo, MemoType: txMemoType, } - switch op.Body.Type { case xdr.OperationTypePayment: fillPayment(&payment, op.Body) @@ -176,6 +177,10 @@ func (m *ingestService) ingestPayments(ctx context.Context, ledgerTransactions [ default: continue } + err = m.models.Payments.AddPayment(ctx, dbTx, payment) + if err != nil { + return fmt.Errorf("adding payment for ledger %d, tx %s (%d), operation %s (%d): %w", tx.Ledger, tx.Hash, tx.ApplicationOrder, payment.OperationID, opIdx, err) + } } } return nil @@ -282,7 +287,9 @@ func fillPathSend(payment *data.Payment, operation xdr.OperationBody, txResult x payment.DestAssetCode = utils.AssetCode(pathOp.DestAsset) payment.DestAssetIssuer = pathOp.DestAsset.GetIssuer() payment.DestAssetType = pathOp.DestAsset.Type.String() - payment.DestAmount = int64(result.DestAmount()) + if result != (xdr.PathPaymentStrictSendResult{}) { + payment.DestAmount = int64(result.DestAmount()) + } } func fillPathReceive(payment *data.Payment, operation xdr.OperationBody, txResult xdr.TransactionResult, operationIdx int) { @@ -298,19 +305,3 @@ func fillPathReceive(payment *data.Payment, operation xdr.OperationBody, txResul payment.DestAssetType = pathOp.DestAsset.Type.String() payment.DestAmount = int64(pathOp.DestAmount) } - -/* -func fillPathReceive(payment *data.Payment, operation xdr.OperationBody, transaction ingest.LedgerTransaction, operationIdx int) { - pathOp := operation.MustPathPaymentStrictReceiveOp() - result := utils.OperationResult(transaction, operationIdx).MustPathPaymentStrictReceiveResult() - payment.ToAddress = pathOp.Destination.Address() - payment.SrcAssetCode = utils.AssetCode(pathOp.SendAsset) - payment.SrcAssetIssuer = pathOp.SendAsset.GetIssuer() - payment.SrcAssetType = pathOp.SendAsset.Type.String() - payment.SrcAmount = int64(result.SendAmount()) - payment.DestAssetCode = utils.AssetCode(pathOp.DestAsset) - payment.DestAssetIssuer = pathOp.DestAsset.GetIssuer() - payment.DestAssetType = pathOp.DestAsset.Type.String() - payment.DestAmount = int64(pathOp.DestAmount) -} -*/ diff --git a/internal/services/ingest_test.go b/internal/services/ingest_test.go index 5548d88..11ec6bd 100644 --- a/internal/services/ingest_test.go +++ b/internal/services/ingest_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/stellar/go/keypair" + "github.com/stellar/go/txnbuild" "github.com/stellar/go/xdr" "github.com/stellar/wallet-backend/internal/apptracker" "github.com/stellar/wallet-backend/internal/data" @@ -158,3 +160,58 @@ func TestProcessTSSTransactions(t *testing.T) { assert.Equal(t, int32(xdr.TransactionResultCodeTxTooLate), updatedTry.Code) }) } + +func TestIngestPayments(t *testing.T) { + dbt := dbtest.Open(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + models, _ := data.NewModels(dbConnectionPool) + mockAppTracker := apptracker.MockAppTracker{} + mockRPCService := RPCServiceMock{} + mockRouter := tssrouter.MockRouter{} + tssStore, _ := tssstore.NewStore(dbConnectionPool) + ingestService, _ := NewIngestService(models, "ingestionLedger", &mockAppTracker, &mockRPCService, &mockRouter, tssStore) + // test these 3 test cases: OperationTypePayment, OperationTypePathPaymentStrictSend, OperationTypePathPaymentStrictReceive + srcAccount := keypair.MustRandom().Address() + destAccount := keypair.MustRandom().Address() + t.Run("test_op_payment", func(t *testing.T) { + _ = models.Account.Insert(context.Background(), srcAccount) + paymentOp := txnbuild.Payment{ + SourceAccount: srcAccount, + Destination: destAccount, + Amount: "10", + Asset: txnbuild.NativeAsset{}, + } + transaction, _ := txnbuild.NewTransaction(txnbuild.TransactionParams{ + SourceAccount: &txnbuild.SimpleAccount{ + AccountID: keypair.MustRandom().Address(), + }, + Operations: []txnbuild.Operation{&paymentOp}, + Preconditions: txnbuild.Preconditions{TimeBounds: txnbuild.NewTimeout(10)}, + }) + + txEnvXDR, _ := transaction.Base64() + + ledgerTransaction := entities.Transaction{ + Status: entities.SuccessStatus, + Hash: "abcd", + ApplicationOrder: 1, + FeeBump: false, + EnvelopeXDR: txEnvXDR, + ResultXDR: "AAAAAAAAAMj////9AAAAAA==", + Ledger: 1, + } + + ledgerTransactions := []entities.Transaction{ledgerTransaction} + + err := ingestService.ingestPayments(context.Background(), ledgerTransactions) + assert.NoError(t, err) + + payments, _, _, err := models.Payments.GetPaymentsPaginated(context.Background(), srcAccount, "", "", data.ASC, 1) + assert.NoError(t, err) + assert.Equal(t, payments[0].TransactionHash, "abcd") + }) +} diff --git a/soroban_rpc_startup.sh b/soroban_rpc_startup.sh new file mode 100644 index 0000000..b4b2c73 --- /dev/null +++ b/soroban_rpc_startup.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# Handle environment logic for Soroban RPC +if [ "$NETWORK" = "testnet" ]; then + CONFIG_FILE="/config/soroban-rpc-config.toml" +elif [ "$NETWORK" = "prod" ]; then + CONFIG_FILE="/config/soroban-prod-rpc-config.toml" +else + echo "Unknown environment: $NETWORK" + exit 1 +fi + +# Start Soroban RPC +./app/soroban-rpc --config-path "$CONFIG_FILE"