import base64,json,pathlib,socket,subprocess,time,urllib.request,urllib.error
class Node:
 def __init__(self,daemon,name,root):
  self.root=root/name;self.root.mkdir();self.name=name
  with socket.socket() as s:s.bind(('127.0.0.1',0));self.port=s.getsockname()[1]
  self.args=[str(daemon),'-regtest','-datadir='+str(self.root),'-rpcport='+str(self.port),'-rpcbind=127.0.0.1','-listen=0','-connect=0','-dnsseed=0','-discover=0','-upnp=0','-txindex=1','-fallbackfee=0.00002','-printtoconsole=0']
  self.start()
 def start(self,replacement=False):
  self.process=subprocess.Popen(self.args+(['-mempoolreplacement=1'] if replacement else []),stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,creationflags=getattr(subprocess,'CREATE_NO_WINDOW',0))
  for _ in range(120):
   try:self.rpc('getblockchaininfo');break
   except Exception:time.sleep(.25)
  else:raise RuntimeError('Node did not start')
 def rpc(self,method,*args,wallet=None):
  cookie=(self.root/'regtest/.cookie').read_text().strip()
  request=urllib.request.Request('http://127.0.0.1:'+str(self.port)+('/wallet/'+wallet if wallet else '/'),data=json.dumps({'jsonrpc':'1.0','id':'lab','method':method,'params':args}).encode(),headers={'Authorization':'Basic '+base64.b64encode(cookie.encode()).decode(),'Content-Type':'application/json'})
  try:
   with urllib.request.urlopen(request,timeout=60) as r:data=json.load(r)
  except urllib.error.HTTPError as e:data=json.loads(e.read())
  if data['error']:raise RuntimeError(method+': '+str(data['error']))
  return data['result']
 def stop(self):
  try:self.rpc('stop')
  except Exception:self.process.terminate()
  self.process.wait(timeout=30)
