
    #Cj-                     f   U d Z ddlZddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlZddlmZmZmZ ddlmZ  ej        dd          Zeed	<    ej        d
d          Zeed<    e ej        dd                    Zeed<    ej        dd          Zeed<    ej        dd          Zeed<    ej        dd          Zeed<    e ej        dd                    Zeed<    e ej        dd                    Zeed<    e ej        dd                    Zeed <    ej         ej!        d!"            ej"        d#          Z#da$ej%        dz  ed$<   ed%efd&            Z& ee&'          Z'd(ed)efd*Z(d(ed+ed)e)fd,Z*d-ed)efd.Z+d/ed)efd0Z,	 dFd2ed3ed4ed5ed6edz  d7ed8ed)dfd9Z-e'.                    d:e;           ed<           ed<           ed<           ed1           ed<          fd=ed>ed?ed@edAedBefdC            Z/e'0                    dDe;          dE             Z1dS )Gu  
tsvalid/main.py
Trade Simulator License Validation — FastAPI endpoint

Request (form-encoded POST body):
    k   license key (plaintext, HMAC-protected)
    m   machine fingerprint (SHA-256 of MT5 install paths)
    t   client epoch timestamp
    b   EA build string
    s   HMAC-SHA256(secret, k|m|t|b)

Response (plain text, pipe-delimited):
    RESULT|server_timestamp|HMAC-SHA256(secret, RESULT|server_timestamp)

Result codes:
    VALID           license ok, proceed
    INVALID         key not found, HMAC failure, or bad request
    EXPIRED         key found but past expiry date
    COOLDOWN        valid key but machine-transfer cooldown active
    INACTIVE        key administratively disabled
    ERROR           server-side error
    N)asynccontextmanager)datetime	timedeltatimezone)FastAPIFormRequestPlainTextResponseLV_HMAC_SECRETCHANGE_THISHMAC_SECRET
LV_DB_HOSTz	127.0.0.1DB_HOST
LV_DB_PORT3306DB_PORT
LV_DB_USERlicuserDB_USER
LV_DB_PASSDB_PASS
LV_DB_NAMETradeSimulatorLicenseDB_NAME
LV_TS_SKEW300TS_SKEW_LIMITLV_COOLDOWN_DAYS7COOLDOWN_DAYSLV_MIN_RESPONSE_MS200MIN_RESPONSE_MSz'%(asctime)s [%(levelname)s] %(message)s)levelformattsvalid_poolappc                  K   t                               dt          t          t                     t          j        t          t          t          t          t          dddd	  	         d {V a	t                               d           d W V  t          
                                 t                                           d {V  t                               d           d S )	Nz"Connecting to MariaDB %s:%s/%s ...utf8mb4F   
   )	hostportuserpassworddbcharset
autocommitminsizemaxsizezDB pool ready.zDB pool closed.)loginfor   r   r   aiomysqlcreate_poolr   r   r(   closewait_closed)r)   s    /wd/tradesimulator/main.pylifespanr>   C   s       HH17GWMMM&7w        E HH	EEEE	KKMMM




HH    )r>   messagereturnc                     t          j        t                                          |                                 t          j                                                  S N)hmacnewr   encodehashlibsha256	hexdigest)r@   s    r=   _hmacrJ   ]   sB    8  ikk	r?   receivedc                     t          |                                           }t          j        ||                                                                          S rC   )rJ   rF   rD   compare_digeststrip)r@   rK   expecteds      r=   _verify_hmacrP   e   sB    W~~$$&&Hx)9)9)@)@)B)BCCCr?   raw_keyc                 r    t          j        |                                                                           S rC   )rG   rH   rF   rI   )rQ   s    r=   	_key_hashrS   j   s(    >'..**++55777r?   codec                     t          t          t          j                                        }t          |  d|           }t	          |  d| d|           S )N|)strinttimerJ   r   )rT   tssigs      r=   _signed_responser\   r   sX    
c$)++

B
4"

C11r11C11222r?    key_hashmachinebuildip	client_tsresultnotec                   K   |d d         }	 t                                           4 d {V }|                                4 d {V }|                    d| |d d         |||||f           d {V  d d d           d {V  n# 1 d {V swxY w Y   |                                 d {V  d d d           d {V  d S # 1 d {V swxY w Y   d S # t
          $ r&}	t                              d|	           Y d }	~	d S d }	~	ww xY w)N@   z
                    INSERT INTO audit_log
                        (key_hash, account, build, ip, client_ts, result, note)
                    VALUES (%s, %s, %s, %s, %s, %s, %s)
                       zaudit_log write failed: %s)r(   acquirecursorexecutecommit	Exceptionr7   error)
r^   r_   r`   ra   rb   rc   rd   conncurexcs
             r=   _auditrq   |   s\      #2#JE5==?? 	  	  	  	  	  	  	 d{{}} 
 
 
 
 
 
 
kk wss|UB	64P	 	 	 	 	 	 	 	 	
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 ++--	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	   5 5 5		.4444444445s^   C C,B4C
B	CB	C1C 
CC CC 
D!DDz/tsvalid)response_class.requestkmtbsc           	        K   t          j                    | j                            d| j        j                                      d          d                                         |d d                                         }d d                                         |d d                                         }d d                                         |d d                                         }t          |          t          
                    d	           d dd ld2dt          dt          dt          ffd}	 t          |          n"# t          $ r  |dd           d {V cY S w xY wt!          t          t          j                               z
            }|t"          k    r |dd| d           d {V S t%          | d d| d |          s.t                              d            |dd           d {V S t)                    dk    st+          d D                       s |dd           d {V S 	 t,                                          4 d {V }|                    t2          j                  4 d {V }	|	                    df           d {V  |	                                 d {V }
|
6 |dd           d {V cd d d           d {V  cd d d           d {V  S |
d         s6 |dd           d {V cd d d           d {V  cd d d           d {V  S |
d         }t;          j        t>          j                   !                    d           }|
d         |k     r? |dd |
d                     d {V cd d d           d {V  cd d d           d {V  S |
d!         }|
d"         }|t|	                    d#|
d$         f           d {V  |"                                 d {V   |d%d&           d {V cd d d           d {V  cd d d           d {V  S t          |          k    r6 |d%d'           d {V cd d d           d {V  cd d d           d {V  S ||tG          tH          (          z   }t;          j        t>          j                   !                    d           }||k     rF||z
  j%        d)z   } |d*d+|            d {V cd d d           d {V  cd d d           d {V  S |	                    d#|
d$         f           d {V  |"                                 d {V   |d%d,t          |          d d-          d.           d {V cd d d           d {V  cd d d           d {V  S # 1 d {V swxY w Y   	 d d d           d {V  d S # 1 d {V swxY w Y   d S # tL          $ rL}t          '                    d/|            |d0d1tQ          |          j)                    d {V cY d }~S d }~ww xY w)3NzX-Forwarded-For,r      rf   rg       P   u>   Request  ip=%-18s  machine=%.16s…  build=%-16s  key=%.12s…r]   rT   rd   rA   c           	      ,  K   t          | |           d {V  t                              d| |           t          j                    	z
  dz  }|t
          k     r&                    t
          |z
  dz             d {V  t          |           S )Nu5   Response ip=%-18s  machine=%.16s…  code=%s  note=%si  )rq   r7   r8   rY   	monotonicr$   sleepr\   )
rT   rd   
elapsed_msasynciorw   	client_ipclient_ts_intkhru   t_starts
      r=   respondzvalidate.<locals>.respond   s      RAy-tDDDDDDDDDHAtT	+ 	+ 	+n&&0D8
''--:!= EFFFFFFFFF%%%r?   INVALIDBAD_TSzTS_SKEW:rx   rV   u   HMAC fail  ip=%s  key=%.12s…	HMAC_FAILc              3      K   | ]}|d v V  	dS )0123456789abcdefN ).0cs     r=   	<genexpr>zvalidate.<locals>.<genexpr>   s(      BB1q$66BBBBBBr?   BAD_MACHINEz
                    SELECT id, registered_account, account_registered_at,
                           expires_at, is_active
                    FROM   licenses
                    WHERE  key_hash = %s
                    	NOT_FOUND	is_activeINACTIVEADMIN_DISABLED
expires_at)tzinfoEXPIREDzEXPIRED_AT:registered_accountaccount_registered_atz
                        UPDATE licenses
                        SET    registered_account    = %s,
                               account_registered_at = NOW()
                        WHERE  id = %s
                        idVALIDFIRST_ACTIVATIONKNOWN_MACHINE)days   COOLDOWNz
DAYS_LEFT:zTRANSFER_FROM:   u   …zDB error during validation: %sERRORzDB_EX:r]   )*rY   r   headersgetclientr.   splitrN   rS   r7   r8   r   rW   r   rX   
ValueErrorabsr   rP   warninglenallr(   rh   ri   r9   
DictCursorrj   fetchoner   nowr   utcreplacerk   r   r!   r   rl   	exceptiontype__name__)rs   rt   ru   rv   rw   rx   r   skewrn   ro   rownow_utcreg_machinereg_timecooldown_end	days_leftrp   r   r   r   r   r   s     ` `            @@@@@r=   validater      s      nG##$5w~7JKKI$$Q'--//I 	
$3$A	#2#A	#2#A	#2#A	#2#A	1BHHM1b" " " !%MNNN& &C &s &4E & & & & & & & & & & & &2A 2 2 2WY111111111112 s49;;-/00DmWY(:4(:(:(:;;;;;;;;; 1**q**1**q**A.. 54iDDDWY444444444 1vv||3BBBBBBB|WY666666666OE==?? J	_ J	_ J	_ J	_ J	_ J	_ J	_d{{8#677 I_ I_ I_ I_ I_ I_ I_3kk E          LLNN****** ;!(K!@!@@@@@@@I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_$ ;' G!(5E!F!FFFFFFF%I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_, |$0&l8<88@@@MMG<(722%,WY-N3|;L-N-N&P &P  P  P  P  P  P  P1I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_< ""67!"9:&++ CI         ++--'''''''!(2D!E!EEEEEEEYI_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_^ %%**!(/!B!BBBBBBBaI_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_j  +'/)2O2O2O'O"*,x|"<"<"D"DD"D"Q"Q"\11)5)?(E(II)0 * 8Y 8 8* * $ $ $ $ $ $sI_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_@ ++ CI         ++--'''''''!(2]3{CSCSTWUWTWCX2]2]2]!^!^^^^^^^SI_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_ I_J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_ J	_X  E E E6<<<WW&CtCyy/A&C&CDDDDDDDDDDDDDDEs   E! !F ?F X  ;&W-!AW,W->X  W*W-<X  A W/W-X  A!W5W-X  $W>W-X  #A3WW-(X  ;A(W#W-5X  
W	W-W	W-X  -
W77X  :W7;X   
Y
AYYYz/tsvalid/healthc                  $   K   t          d          S )NOKr
   r   r?   r=   healthr   /  s      T"""r?   r   )2__doc__rG   rD   loggingosrY   
contextlibr   r   r   r   r9   fastapir   r   r	   fastapi.responsesr   getenvr   rW   __annotations__r   rX   r   r   r   r   r   r!   floatr$   basicConfigINFO	getLoggerr7   r(   Poolr>   r)   rJ   boolrP   rS   r\   rq   postr   r   r   r   r?   r=   <module>r      st    .    				  * * * * * * 2 2 2 2 2 2 2 2 2 2  * * * * * * * * * * / / / / / / ##3=II I I I"<KHH H H HYRY|FCCDD D D D"<IFF F F F"<MJJ J J J"<<STT T T TYRY|EBBCC C C CYRY'9C@@AA A A Ayry)=uEEFF F F F  
,4    g	"" #x}t " " "          & gx   3 3    D# D D D D D D
8s 8s 8 8 8 833 3#4 3 3 3 3" 5 555 5 	5
 Tz5 5 5 
5 5 5 5D *%677 T#YYT#YYT#YYT"XXT#YYIE IEIE
IE IE 	IE
 IE IE IE IE 87IE` 	+<==# # >=# # #r?   