
    iDj                        d Z ddlZddlmZ ddlmZmZ ddlmZ ddl	m
Z
mZ ddlmZmZmZmZ ddlmZmZmZ dd	lmZ dd
lmZmZ edefd            Z ede          Zdededz  defdZe                    d           ed           ed           ed           ed           ed           ed          fdedededededefd            Z e!                    d          d             Z"dS )uz	  
TD Slope params server.

Endpoint: POST /params

Request body (application/x-www-form-urlencoded), deliberately mirroring
LicenseValidator.mqh's _LV_DoValidate() request shape so the MT5-side
code can reuse the exact same WebRequest()/HMAC-construction pattern it
already has, just pointed at a different URL with one extra field:

    k   = license key
    m   = machine ID (same SHA-256 fingerprint the license check uses)
    t   = unix timestamp (GMT)
    b   = build string (compiled EA filename + compile date)
    sym = the symbol as reported by MQL5's _Symbol on the EA's chart
    s   = HMAC-SHA256(secret, "k|m|t|b|sym") — note the trailing |sym
          compared to the license endpoint's "k|m|t|b"; the field is
          appended, not inserted, to keep this easy to reason about as
          "license signing logic plus one more field" rather than a
          divergent scheme.

Response body (application/json):
    {
      "code": "OK" | "NOT_AVAILABLE" | "UNKNOWN_SYMBOL"
            | "INVALID" | "EXPIRED" | "COOLDOWN" | "INACTIVE"
            | "BAD_SIGNATURE" | "TIMESTAMP_SKEW" | "MALFORMED_REQUEST",
      "ts": <server unix timestamp>,
      "params": "<json-escaped string, full week-doc, empty if code != OK>",
      "sig": "<hex HMAC-SHA256(secret, code + '|' + ts + '|' + params_string)>"
    }

The "params" field is deliberately sent as a JSON-encoded STRING (an
escaped blob) rather than a nested JSON object. This means the MT5 client
only needs a trivial flat-key parser for the OUTER envelope (4 fixed
top-level string/number fields — about as complex as the existing
pipe-delimited license response), and can verify the signature by hashing
the exact literal text of that inner string after un-escaping it, with no
risk of a canonicalization mismatch between Python's json.dumps and a
hand-written MQL5 JSON serializer ever needing to agree byte-for-byte.
Parsing the INNER json (the actual params) into real values is a separate,
slightly bigger problem the EA needs a small JSON parser for regardless —
that's expected and fine, since the params blob itself needs to become
real input values either way.

code values reuse the SAME vocabulary as LicenseValidator.mqh's existing
codes (INVALID/EXPIRED/COOLDOWN/INACTIVE) for license-related failures, so
the EA can reuse its existing _LV_ShowError()-style alert handling rather
than inventing a second, parallel error-handling path.
    N)asynccontextmanager)FastAPIForm)JSONResponse)PARAMS_SHARED_SECRETMAX_TIMESTAMP_SKEW_SECONDS)hmac_sha256_hexverify_hmac_hexcurrent_unix_tstimestamp_in_skew)license_store	init_pool
close_pool)resolver)get_current_or_upcoming_paramssymbol_has_any_dataappc                d   K   t                       d {V  d W V  t                       d {V  d S )N)r   r   )r   s    /wd/td-params-server/main.pylifespanr   @   sQ      
 ++	EEEE
,,    zTD Slope Params Server)titler   code
params_docreturnc                     t                      }|t          j        |dd          nd}t          t          |  d| d|           }t          | |||d          S )N),:T)
separators	sort_keys |)r   tsparamssig)r   jsondumpsr	   r   r   )r   r   r#   params_stringr%   s        r   _signed_responser)   M   s    			B ! 	
:*EEEE 
 .40N0N"0N0N}0N0N
O
OC#		
 	
  r   z/params.kmtbsymsc           	        K   	 t          |          }n # t          $ r t          dd           cY S w xY w| r|r|st          dd           S t          |t                    st          dd           S |  d| d| d| d| 	}t          t          ||          st          dd           S t          j        | |           d {V }|j	        dk    rt          |j	        d           S t          j        |          }	t          |	          st          dd           S t          |	          }
|
t          dd           S t          d	|
          S )
NMALFORMED_REQUESTTIMESTAMP_SKEWr"   BAD_SIGNATURE)license_key
machine_idVALIDUNKNOWN_SYMBOLNOT_AVAILABLEOK)int
ValueErrorr)   r   r   r
   r   r   checkr   r   resolver   r   )r*   r+   r,   r-   r.   r/   	client_tsexpected_msglic	canonicaldocs              r   
get_paramsrC   _   s     ;FF		 ; ; ; 3T:::::;  ;A ;S ; 3T::: Y(BCC 8 0$777 ++!++a++!++c++L/qAA 7666 #a@@@
@
@
@
@
@
@C
x7$///  %%Iy)) 8 0$777
(
3
3C
{666D#&&&s    11z/healthzc                  (   K   dt                      dS )Nok)statusr#   )r    r   r   healthzrH      s       /"3"3444r   )#__doc__r&   
contextlibr   fastapir   r   fastapi.responsesr   configr   r   crypto_utilsr	   r
   r   r   license_checkr   r   r   symbol_resolverr   params_storer   r   r   r   strdictr)   postrC   getrH   rG   r   r   <module>rV      s&  0 0d  * * * * * * ! ! ! ! ! ! ! ! * * * * * * C C C C C C C C ] ] ] ] ] ] ] ] ] ] ] ] > > > > > > > > > > $ $ $ $ $ $ L L L L L L L L      g,x@@@3 D4K L    $ )T#YYT#YYT#YYT#YYtCyyT#YY)' )'
)'
)' )' 	)'
 
)' )' )' )' )'X 5 5 5 5 5r   